media
This commit is contained in:
parent
f0dfffe9bb
commit
3779c33046
@ -2,10 +2,17 @@ package com.foxinmy.weixin4j.http;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import com.foxinmy.weixin4j.util.CharArrayBuffer;
|
||||
import com.foxinmy.weixin4j.util.Consts;
|
||||
import com.foxinmy.weixin4j.util.NameValue;
|
||||
import com.foxinmy.weixin4j.util.StringUtil;
|
||||
|
||||
/**
|
||||
* reference of apache pivot
|
||||
@ -22,6 +29,7 @@ public final class ContentType implements Serializable {
|
||||
|
||||
private final MimeType mimeType;
|
||||
private final Charset charset;
|
||||
private final NameValue[] params;
|
||||
private static final Charset DEFAULT_CHARSET = Consts.UTF_8;
|
||||
|
||||
public static final ContentType APPLICATION_JSON;
|
||||
@ -32,7 +40,8 @@ public final class ContentType implements Serializable {
|
||||
|
||||
static {
|
||||
APPLICATION_JSON = new ContentType(MimeType.APPLICATION_JSON);
|
||||
APPLICATION_FORM_URLENCODED = new ContentType(MimeType.APPLICATION_FORM_URLENCODED);
|
||||
APPLICATION_FORM_URLENCODED = new ContentType(
|
||||
MimeType.APPLICATION_FORM_URLENCODED);
|
||||
MULTIPART_FORM_DATA = new ContentType(MimeType.MULTIPART_FORM_DATA);
|
||||
DEFAULT_BINARY = new ContentType(MimeType.APPLICATION_OCTET_STREAM);
|
||||
DEFAULT_TEXT = new ContentType(MimeType.TEXT_PLAIN);
|
||||
@ -43,8 +52,14 @@ public final class ContentType implements Serializable {
|
||||
}
|
||||
|
||||
ContentType(final MimeType mimeType, final Charset charset) {
|
||||
this(mimeType, charset, null);
|
||||
}
|
||||
|
||||
ContentType(final MimeType mimeType, final Charset charset,
|
||||
final NameValue[] params) {
|
||||
this.mimeType = mimeType;
|
||||
this.charset = charset;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public MimeType getMimeType() {
|
||||
@ -55,11 +70,34 @@ public final class ContentType implements Serializable {
|
||||
return this.charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
public String getParameter(final String name) {
|
||||
if (this.params == null) {
|
||||
return null;
|
||||
}
|
||||
for (final NameValue param : this.params) {
|
||||
if (param.getName().equalsIgnoreCase(name)) {
|
||||
return param.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates textual representation of this content type which can be used
|
||||
* as the value of a {@code Content-Type} header.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(this.mimeType.toString());
|
||||
if (this.charset != null) {
|
||||
final CharArrayBuffer buf = new CharArrayBuffer(64);
|
||||
buf.append(this.mimeType);
|
||||
if (this.params != null) {
|
||||
buf.append("; ");
|
||||
HeaderValueFormatter.INSTANCE.formatParameters(buf,
|
||||
this.params, false);
|
||||
} else if (this.charset != null) {
|
||||
buf.append("; charset=");
|
||||
buf.append(this.charset.name());
|
||||
}
|
||||
@ -87,7 +125,8 @@ public final class ContentType implements Serializable {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static ContentType create(final MimeType mimeType, final Charset charset) {
|
||||
public static ContentType create(final MimeType mimeType,
|
||||
final Charset charset) {
|
||||
if (mimeType == null) {
|
||||
throw new IllegalArgumentException("MIME type may not be null");
|
||||
}
|
||||
@ -98,7 +137,16 @@ public final class ContentType implements Serializable {
|
||||
return create(MimeType.valueOf(mimeType), (Charset) null);
|
||||
}
|
||||
|
||||
public static ContentType create(final String mimeType, final Charset charset) {
|
||||
public static ContentType create(final String mimeType, final String charset)
|
||||
throws UnsupportedCharsetException {
|
||||
return create(
|
||||
mimeType,
|
||||
(charset != null && charset.length() > 0) ? Charset
|
||||
.forName(charset) : null);
|
||||
}
|
||||
|
||||
public static ContentType create(final String mimeType,
|
||||
final Charset charset) {
|
||||
if (mimeType == null) {
|
||||
throw new IllegalArgumentException("MIME type may not be null");
|
||||
}
|
||||
@ -107,8 +155,84 @@ public final class ContentType implements Serializable {
|
||||
throw new IllegalArgumentException("MIME type may not be empty");
|
||||
}
|
||||
if (!valid(type)) {
|
||||
throw new IllegalArgumentException("MIME type may not contain reserved characters");
|
||||
throw new IllegalArgumentException(
|
||||
"MIME type may not contain reserved characters");
|
||||
}
|
||||
return new ContentType(MimeType.valueOf(type), charset);
|
||||
}
|
||||
|
||||
private static ContentType create(final MimeType mimeType,
|
||||
final NameValue[] params, final boolean strict) {
|
||||
Charset charset = null;
|
||||
for (final NameValue param : params) {
|
||||
if (param.getName().equalsIgnoreCase("charset")) {
|
||||
final String s = param.getValue();
|
||||
if (StringUtil.isNotBlank(s)) {
|
||||
try {
|
||||
charset = Charset.forName(s);
|
||||
} catch (final UnsupportedCharsetException ex) {
|
||||
if (strict) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new ContentType(mimeType, charset, params != null
|
||||
&& params.length > 0 ? params : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link ContentType} with the given parameters.
|
||||
*
|
||||
* @param mimeType MIME type. It may not be {@code null} or empty. It may not contain
|
||||
* characters {@code <">, <;>, <,>} reserved by the HTTP specification.
|
||||
* @param params parameters.
|
||||
* @return content type
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
public static ContentType create(final String mimeType,
|
||||
final NameValue... params) throws UnsupportedCharsetException {
|
||||
final String type = mimeType.toLowerCase(Locale.ROOT);
|
||||
if (!valid(type)) {
|
||||
throw new IllegalArgumentException(
|
||||
"MIME type may not contain reserved characters");
|
||||
}
|
||||
return create(MimeType.valueOf(mimeType), params, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with this MIME type and the given parameters.
|
||||
*
|
||||
* @param params
|
||||
* @return a new instance with this MIME type and the given parameters.
|
||||
* @since 4.4
|
||||
*/
|
||||
public ContentType withParameters(final NameValue... params)
|
||||
throws UnsupportedCharsetException {
|
||||
if (params.length == 0) {
|
||||
return this;
|
||||
}
|
||||
final Map<String, String> paramMap = new LinkedHashMap<String, String>();
|
||||
if (this.params != null) {
|
||||
for (final NameValue param : this.params) {
|
||||
paramMap.put(param.getName(), param.getValue());
|
||||
}
|
||||
}
|
||||
for (final NameValue param : params) {
|
||||
paramMap.put(param.getName(), param.getValue());
|
||||
}
|
||||
final List<NameValue> newParams = new ArrayList<NameValue>(
|
||||
paramMap.size() + 1);
|
||||
if (this.charset != null && !paramMap.containsKey("charset")) {
|
||||
newParams.add(new NameValue("charset", this.charset.name()));
|
||||
}
|
||||
for (final Map.Entry<String, String> entry : paramMap.entrySet()) {
|
||||
newParams.add(new NameValue(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
return create(this.getMimeType(),
|
||||
newParams.toArray(new NameValue[newParams.size()]), true);
|
||||
}
|
||||
}
|
||||
@ -25,8 +25,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
package com.foxinmy.weixin4j.http;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.apache.http.Consts;
|
||||
|
||||
/**
|
||||
* Constants and static helpers related to the HTTP protocol.
|
||||
@ -63,11 +66,52 @@ public final class HTTP {
|
||||
public static final String CHUNK_CODING = "chunked";
|
||||
public static final String IDENTITY_CODING = "identity";
|
||||
|
||||
public static boolean isWhitespace(char ch) {
|
||||
public static final Charset DEF_CONTENT_CHARSET = Consts.ISO_8859_1;
|
||||
public static final Charset DEF_PROTOCOL_CHARSET = Consts.ASCII;
|
||||
|
||||
/**
|
||||
*/
|
||||
public static final String UTF_8 = "UTF-8";
|
||||
|
||||
/**
|
||||
*/
|
||||
public static final String UTF_16 = "UTF-16";
|
||||
|
||||
/**
|
||||
*/
|
||||
public static final String US_ASCII = "US-ASCII";
|
||||
|
||||
/**
|
||||
*/
|
||||
public static final String ASCII = "ASCII";
|
||||
/**
|
||||
*/
|
||||
public static final String DEFAULT_CONTENT_CHARSET = UTF_8;
|
||||
|
||||
/**
|
||||
*/
|
||||
public static final String DEFAULT_PROTOCOL_CHARSET = US_ASCII;
|
||||
|
||||
/**
|
||||
*/
|
||||
public final static String OCTET_STREAM_TYPE = "application/octet-stream";
|
||||
|
||||
/**
|
||||
*/
|
||||
public final static String PLAIN_TEXT_TYPE = "text/plain";
|
||||
|
||||
/**
|
||||
*/
|
||||
public final static String CHARSET_PARAM = "; charset=";
|
||||
|
||||
/**
|
||||
*/
|
||||
public final static String DEFAULT_CONTENT_TYPE = OCTET_STREAM_TYPE;
|
||||
|
||||
public static boolean isWhitespace(final char ch) {
|
||||
return ch == SP || ch == HT || ch == CR || ch == LF;
|
||||
}
|
||||
|
||||
private HTTP() {
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http;
|
||||
|
||||
import com.foxinmy.weixin4j.util.CharArrayBuffer;
|
||||
import com.foxinmy.weixin4j.util.NameValue;
|
||||
|
||||
/**
|
||||
* Basic implementation for formatting header value elements.
|
||||
* Instances of this class are stateless and thread-safe.
|
||||
* Derived classes are expected to maintain these properties.
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class HeaderValueFormatter {
|
||||
|
||||
public final static HeaderValueFormatter INSTANCE = new HeaderValueFormatter();
|
||||
|
||||
/**
|
||||
* Special characters that can be used as separators in HTTP parameters.
|
||||
* These special characters MUST be in a quoted string to be used within
|
||||
* a parameter value .
|
||||
*/
|
||||
public final static String SEPARATORS = " ;,:@()<>\\\"/[]?={}\t";
|
||||
|
||||
/**
|
||||
* Unsafe special characters that must be escaped using the backslash
|
||||
* character
|
||||
*/
|
||||
public final static String UNSAFE_CHARS = "\"\\";
|
||||
|
||||
public HeaderValueFormatter() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a set of parameters.
|
||||
*
|
||||
* @param nvps the parameters to format
|
||||
* @param quote <code>true</code> to always format with quoted values,
|
||||
* <code>false</code> to use quotes only when necessary
|
||||
* @param formatter the formatter to use, or <code>null</code>
|
||||
* for the {@link #INSTANCE default}
|
||||
*
|
||||
* @return the formatted parameters
|
||||
*/
|
||||
public static
|
||||
String formatParameters(final NameValue[] nvps,
|
||||
final boolean quote,
|
||||
final HeaderValueFormatter formatter) {
|
||||
return (formatter != null ? formatter : HeaderValueFormatter.INSTANCE)
|
||||
.formatParameters(null, nvps, quote).toString();
|
||||
}
|
||||
|
||||
|
||||
// non-javadoc, see interface HeaderValueFormatter
|
||||
public CharArrayBuffer formatParameters(final CharArrayBuffer charBuffer,
|
||||
final NameValue[] nvps,
|
||||
final boolean quote) {
|
||||
final int len = estimateParametersLen(nvps);
|
||||
CharArrayBuffer buffer = charBuffer;
|
||||
if (buffer == null) {
|
||||
buffer = new CharArrayBuffer(len);
|
||||
} else {
|
||||
buffer.ensureCapacity(len);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nvps.length; i++) {
|
||||
if (i > 0) {
|
||||
buffer.append("; ");
|
||||
}
|
||||
formatNameValuePair(buffer, nvps[i], quote);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Estimates the length of formatted parameters.
|
||||
*
|
||||
* @param nvps the parameters to format, or <code>null</code>
|
||||
*
|
||||
* @return a length estimate, in number of characters
|
||||
*/
|
||||
protected int estimateParametersLen(final NameValue[] nvps) {
|
||||
if ((nvps == null) || (nvps.length < 1)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = (nvps.length-1) * 2; // "; " between the parameters
|
||||
for (final NameValue nvp : nvps) {
|
||||
result += estimateNameValuePairLen(nvp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats a name-value pair.
|
||||
*
|
||||
* @param nvp the name-value pair to format
|
||||
* @param quote <code>true</code> to always format with a quoted value,
|
||||
* <code>false</code> to use quotes only when necessary
|
||||
* @param formatter the formatter to use, or <code>null</code>
|
||||
* for the {@link #INSTANCE default}
|
||||
*
|
||||
* @return the formatted name-value pair
|
||||
*/
|
||||
public static
|
||||
String formatNameValuePair(final NameValue nvp,
|
||||
final boolean quote,
|
||||
final HeaderValueFormatter formatter) {
|
||||
return (formatter != null ? formatter : HeaderValueFormatter.INSTANCE)
|
||||
.formatNameValuePair(null, nvp, quote).toString();
|
||||
}
|
||||
|
||||
|
||||
// non-javadoc, see interface HeaderValueFormatter
|
||||
public CharArrayBuffer formatNameValuePair(final CharArrayBuffer charBuffer,
|
||||
final NameValue nvp,
|
||||
final boolean quote) {
|
||||
final int len = estimateNameValuePairLen(nvp);
|
||||
CharArrayBuffer buffer = charBuffer;
|
||||
if (buffer == null) {
|
||||
buffer = new CharArrayBuffer(len);
|
||||
} else {
|
||||
buffer.ensureCapacity(len);
|
||||
}
|
||||
|
||||
buffer.append(nvp.getName());
|
||||
final String value = nvp.getValue();
|
||||
if (value != null) {
|
||||
buffer.append('=');
|
||||
doFormatValue(buffer, value, quote);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Estimates the length of a formatted name-value pair.
|
||||
*
|
||||
* @param nvp the name-value pair to format, or <code>null</code>
|
||||
*
|
||||
* @return a length estimate, in number of characters
|
||||
*/
|
||||
protected int estimateNameValuePairLen(final NameValue nvp) {
|
||||
if (nvp == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = nvp.getName().length(); // name
|
||||
final String value = nvp.getValue();
|
||||
if (value != null) {
|
||||
// assume quotes, but no escaped characters
|
||||
result += 3 + value.length(); // ="value"
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Actually formats the value of a name-value pair.
|
||||
* This does not include a leading = character.
|
||||
* Called from {@link #formatNameValuePair formatNameValuePair}.
|
||||
*
|
||||
* @param buffer the buffer to append to, never <code>null</code>
|
||||
* @param value the value to append, never <code>null</code>
|
||||
* @param quote <code>true</code> to always format with quotes,
|
||||
* <code>false</code> to use quotes only when necessary
|
||||
*/
|
||||
protected void doFormatValue(final CharArrayBuffer buffer,
|
||||
final String value,
|
||||
final boolean quote) {
|
||||
|
||||
boolean quoteFlag = quote;
|
||||
if (!quoteFlag) {
|
||||
for (int i = 0; (i < value.length()) && !quoteFlag; i++) {
|
||||
quoteFlag = isSeparator(value.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (quoteFlag) {
|
||||
buffer.append('"');
|
||||
}
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
final char ch = value.charAt(i);
|
||||
if (isUnsafe(ch)) {
|
||||
buffer.append('\\');
|
||||
}
|
||||
buffer.append(ch);
|
||||
}
|
||||
if (quoteFlag) {
|
||||
buffer.append('"');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether a character is a {@link #SEPARATORS separator}.
|
||||
*
|
||||
* @param ch the character to check
|
||||
*
|
||||
* @return <code>true</code> if the character is a separator,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
protected boolean isSeparator(final char ch) {
|
||||
return SEPARATORS.indexOf(ch) >= 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether a character is {@link #UNSAFE_CHARS unsafe}.
|
||||
*
|
||||
* @param ch the character to check
|
||||
*
|
||||
* @return <code>true</code> if the character is unsafe,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
protected boolean isUnsafe(final char ch) {
|
||||
return UNSAFE_CHARS.indexOf(ch) >= 0;
|
||||
}
|
||||
|
||||
|
||||
} // class BasicHeaderValueFormatter
|
||||
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
|
||||
/**
|
||||
* Represents common content properties.
|
||||
*/
|
||||
public interface ContentDescriptor {
|
||||
|
||||
/**
|
||||
* Returns the body descriptors MIME type.
|
||||
* @see #getMediaType()
|
||||
* @see #getSubType()
|
||||
* @return The MIME type, which has been parsed from the
|
||||
* content-type definition. Must not be null, but
|
||||
* "text/plain", if no content-type was specified.
|
||||
*/
|
||||
String getMimeType();
|
||||
|
||||
/**
|
||||
* Gets the defaulted MIME media type for this content.
|
||||
* For example <code>TEXT</code>, <code>IMAGE</code>, <code>MULTIPART</code>
|
||||
* @see #getMimeType()
|
||||
* @return the MIME media type when content-type specified,
|
||||
* otherwise the correct default (<code>TEXT</code>)
|
||||
*/
|
||||
String getMediaType();
|
||||
|
||||
/**
|
||||
* Gets the defaulted MIME sub type for this content.
|
||||
* @see #getMimeType()
|
||||
* @return the MIME media type when content-type is specified,
|
||||
* otherwise the correct default (<code>PLAIN</code>)
|
||||
*/
|
||||
String getSubType();
|
||||
|
||||
/**
|
||||
* <p>The body descriptors character set, defaulted appropriately for the MIME type.</p>
|
||||
* <p>
|
||||
* For <code>TEXT</code> types, this will be defaulted to <code>us-ascii</code>.
|
||||
* For other types, when the charset parameter is missing this property will be null.
|
||||
* </p>
|
||||
* @return Character set, which has been parsed from the
|
||||
* content-type definition. Not null for <code>TEXT</code> types, when unset will
|
||||
* be set to default <code>us-ascii</code>. For other types, when unset,
|
||||
* null will be returned.
|
||||
*/
|
||||
String getCharset();
|
||||
|
||||
/**
|
||||
* Returns the body descriptors transfer encoding.
|
||||
* @return The transfer encoding. Must not be null, but "7bit",
|
||||
* if no transfer-encoding was specified.
|
||||
*/
|
||||
String getTransferEncoding();
|
||||
|
||||
/**
|
||||
* Returns the body descriptors content-length.
|
||||
* @return Content length, if known, or -1, to indicate the absence of a
|
||||
* content-length header.
|
||||
*/
|
||||
long getContentLength();
|
||||
|
||||
}
|
||||
@ -1,146 +0,0 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import com.foxinmy.weixin4j.util.NameValue;
|
||||
|
||||
/**
|
||||
* The header of an entity (see RFC 2045).
|
||||
*/
|
||||
public class Header implements Iterable<NameValue> {
|
||||
|
||||
private final List<NameValue> fields;
|
||||
private final Map<String, List<NameValue>> fieldMap;
|
||||
|
||||
public Header() {
|
||||
super();
|
||||
this.fields = new LinkedList<NameValue>();
|
||||
this.fieldMap = new HashMap<String, List<NameValue>>();
|
||||
}
|
||||
|
||||
public void addField(final NameValue field) {
|
||||
if (field == null) {
|
||||
return;
|
||||
}
|
||||
String key = field.getName().toLowerCase(Locale.US);
|
||||
List<NameValue> values = this.fieldMap.get(key);
|
||||
if (values == null) {
|
||||
values = new LinkedList<NameValue>();
|
||||
this.fieldMap.put(key, values);
|
||||
}
|
||||
values.add(field);
|
||||
this.fields.add(field);
|
||||
}
|
||||
|
||||
public List<NameValue> getFields() {
|
||||
return new ArrayList<NameValue>(this.fields);
|
||||
}
|
||||
|
||||
public NameValue getField(final String name) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
String key = name.toLowerCase(Locale.US);
|
||||
List<NameValue> list = this.fieldMap.get(key);
|
||||
if (list != null && !list.isEmpty()) {
|
||||
return list.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<NameValue> getFields(final String name) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
String key = name.toLowerCase(Locale.US);
|
||||
List<NameValue> list = this.fieldMap.get(key);
|
||||
if (list == null || list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
return new ArrayList<NameValue>(list);
|
||||
}
|
||||
}
|
||||
|
||||
public int removeFields(final String name) {
|
||||
if (name == null) {
|
||||
return 0;
|
||||
}
|
||||
String key = name.toLowerCase(Locale.US);
|
||||
List<NameValue> removed = fieldMap.remove(key);
|
||||
if (removed == null || removed.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
this.fields.removeAll(removed);
|
||||
return removed.size();
|
||||
}
|
||||
|
||||
public void setField(final NameValue field) {
|
||||
if (field == null) {
|
||||
return;
|
||||
}
|
||||
String key = field.getName().toLowerCase(Locale.US);
|
||||
List<NameValue> list = fieldMap.get(key);
|
||||
if (list == null || list.isEmpty()) {
|
||||
addField(field);
|
||||
return;
|
||||
}
|
||||
list.clear();
|
||||
list.add(field);
|
||||
int firstOccurrence = -1;
|
||||
int index = 0;
|
||||
for (Iterator<NameValue> it = this.fields.iterator(); it.hasNext(); index++) {
|
||||
NameValue f = it.next();
|
||||
if (f.getName().equalsIgnoreCase(field.getName())) {
|
||||
it.remove();
|
||||
if (firstOccurrence == -1) {
|
||||
firstOccurrence = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.fields.add(firstOccurrence, field);
|
||||
}
|
||||
|
||||
public Iterator<NameValue> iterator() {
|
||||
return Collections.unmodifiableList(fields).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.fields.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,278 +0,0 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.foxinmy.weixin4j.util.NameValue;
|
||||
|
||||
/**
|
||||
* HttpMultipart represents a collection of MIME multipart encoded content
|
||||
* bodies. This class is capable of operating either in the strict (RFC 822, RFC
|
||||
* 2045, RFC 2046 compliant) or the browser compatible modes.
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class HttpMultipart {
|
||||
|
||||
private static ByteArrayBuffer encode(final Charset charset,
|
||||
final String string) {
|
||||
ByteBuffer encoded = charset.encode(CharBuffer.wrap(string));
|
||||
ByteArrayBuffer bab = new ByteArrayBuffer(encoded.remaining());
|
||||
bab.append(encoded.array(), encoded.position(), encoded.remaining());
|
||||
return bab;
|
||||
}
|
||||
|
||||
private static void writeBytes(final ByteArrayBuffer b,
|
||||
final OutputStream out) throws IOException {
|
||||
out.write(b.buffer(), 0, b.length());
|
||||
}
|
||||
|
||||
private static void writeBytes(final String s, final Charset charset,
|
||||
final OutputStream out) throws IOException {
|
||||
ByteArrayBuffer b = encode(charset, s);
|
||||
writeBytes(b, out);
|
||||
}
|
||||
|
||||
private static void writeBytes(final String s, final OutputStream out)
|
||||
throws IOException {
|
||||
ByteArrayBuffer b = encode(MIME.DEFAULT_CHARSET, s);
|
||||
writeBytes(b, out);
|
||||
}
|
||||
|
||||
private static void writeField(final NameValue field, final OutputStream out)
|
||||
throws IOException {
|
||||
writeBytes(field.getName(), out);
|
||||
writeBytes(FIELD_SEP, out);
|
||||
writeBytes(field.getValue(), out);
|
||||
writeBytes(CR_LF, out);
|
||||
}
|
||||
|
||||
private static void writeField(final NameValue field, final Charset charset,
|
||||
final OutputStream out) throws IOException {
|
||||
writeBytes(field.getName(), charset, out);
|
||||
writeBytes(FIELD_SEP, out);
|
||||
writeBytes(field.getValue(), charset, out);
|
||||
writeBytes(CR_LF, out);
|
||||
}
|
||||
|
||||
private static final ByteArrayBuffer FIELD_SEP = encode(
|
||||
MIME.DEFAULT_CHARSET, ": ");
|
||||
private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET,
|
||||
"\r\n");
|
||||
private static final ByteArrayBuffer TWO_DASHES = encode(
|
||||
MIME.DEFAULT_CHARSET, "--");
|
||||
|
||||
private final String subType;
|
||||
private final Charset charset;
|
||||
private final String boundary;
|
||||
private final List<FormBodyPart> parts;
|
||||
|
||||
private final HttpMultipartMode mode;
|
||||
|
||||
/**
|
||||
* Creates an instance with the specified settings.
|
||||
*
|
||||
* @param subType
|
||||
* mime subtype - must not be {@code null}
|
||||
* @param charset
|
||||
* the character set to use. May be {@code null}, in which case
|
||||
* {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
||||
* @param boundary
|
||||
* to use - must not be {@code null}
|
||||
* @param mode
|
||||
* the mode to use
|
||||
* @throws IllegalArgumentException
|
||||
* if charset is null or boundary is null
|
||||
*/
|
||||
public HttpMultipart(final String subType, final Charset charset,
|
||||
final String boundary, HttpMultipartMode mode) {
|
||||
super();
|
||||
if (subType == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Multipart subtype may not be null");
|
||||
}
|
||||
if (boundary == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Multipart boundary may not be null");
|
||||
}
|
||||
this.subType = subType;
|
||||
this.charset = charset != null ? charset : MIME.DEFAULT_CHARSET;
|
||||
this.boundary = boundary;
|
||||
this.parts = new ArrayList<FormBodyPart>();
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with the specified settings. Mode is set to
|
||||
* {@link HttpMultipartMode#STRICT}
|
||||
*
|
||||
* @param subType
|
||||
* mime subtype - must not be {@code null}
|
||||
* @param charset
|
||||
* the character set to use. May be {@code null}, in which case
|
||||
* {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
||||
* @param boundary
|
||||
* to use - must not be {@code null}
|
||||
* @throws IllegalArgumentException
|
||||
* if charset is null or boundary is null
|
||||
*/
|
||||
public HttpMultipart(final String subType, final Charset charset,
|
||||
final String boundary) {
|
||||
this(subType, charset, boundary, HttpMultipartMode.STRICT);
|
||||
}
|
||||
|
||||
public HttpMultipart(final String subType, final String boundary) {
|
||||
this(subType, null, boundary);
|
||||
}
|
||||
|
||||
public String getSubType() {
|
||||
return this.subType;
|
||||
}
|
||||
|
||||
public Charset getCharset() {
|
||||
return this.charset;
|
||||
}
|
||||
|
||||
public HttpMultipartMode getMode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
public List<FormBodyPart> getBodyParts() {
|
||||
return this.parts;
|
||||
}
|
||||
|
||||
public void addBodyPart(final FormBodyPart part) {
|
||||
if (part == null) {
|
||||
return;
|
||||
}
|
||||
this.parts.add(part);
|
||||
}
|
||||
|
||||
public String getBoundary() {
|
||||
return this.boundary;
|
||||
}
|
||||
|
||||
private void doWriteTo(final HttpMultipartMode mode,
|
||||
final OutputStream out, boolean writeContent) throws IOException {
|
||||
|
||||
ByteArrayBuffer boundary = encode(this.charset, getBoundary());
|
||||
for (FormBodyPart part : this.parts) {
|
||||
writeBytes(TWO_DASHES, out);
|
||||
writeBytes(boundary, out);
|
||||
writeBytes(CR_LF, out);
|
||||
|
||||
Header header = part.getHeader();
|
||||
|
||||
switch (mode) {
|
||||
case STRICT:
|
||||
for (NameValue field : header) {
|
||||
writeField(field, out);
|
||||
}
|
||||
break;
|
||||
case BROWSER_COMPATIBLE:
|
||||
// Only write Content-Disposition
|
||||
// Use content charset
|
||||
NameValue cd = part.getHeader().getField(
|
||||
MIME.CONTENT_DISPOSITION);
|
||||
writeField(cd, this.charset, out);
|
||||
String filename = part.getBody().getFilename();
|
||||
if (filename != null) {
|
||||
NameValue ct = part.getHeader().getField(MIME.CONTENT_TYPE);
|
||||
writeField(ct, this.charset, out);
|
||||
}
|
||||
break;
|
||||
}
|
||||
writeBytes(CR_LF, out);
|
||||
|
||||
if (writeContent) {
|
||||
part.getBody().writeTo(out);
|
||||
}
|
||||
writeBytes(CR_LF, out);
|
||||
}
|
||||
writeBytes(TWO_DASHES, out);
|
||||
writeBytes(boundary, out);
|
||||
writeBytes(TWO_DASHES, out);
|
||||
writeBytes(CR_LF, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out the content in the multipart/form encoding. This method
|
||||
* produces slightly different formatting depending on its compatibility
|
||||
* mode.
|
||||
*
|
||||
* @see #getMode()
|
||||
*/
|
||||
public void writeTo(final OutputStream out) throws IOException {
|
||||
doWriteTo(this.mode, out, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the total length of the multipart content (content length of
|
||||
* individual parts plus that of extra elements required to delimit the
|
||||
* parts from one another). If any of the @{link BodyPart}s contained in
|
||||
* this object is of a streaming entity of unknown length the total length
|
||||
* is also unknown.
|
||||
* <p/>
|
||||
* This method buffers only a small amount of data in order to determine the
|
||||
* total length of the entire entity. The content of individual parts is not
|
||||
* buffered.
|
||||
*
|
||||
* @return total length of the multipart entity if known, <code>-1</code>
|
||||
* otherwise.
|
||||
*/
|
||||
public long getTotalLength() {
|
||||
long contentLen = 0;
|
||||
for (FormBodyPart part : this.parts) {
|
||||
ContentBody body = part.getBody();
|
||||
long len = body.getContentLength();
|
||||
if (len >= 0) {
|
||||
contentLen += len;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
doWriteTo(this.mode, out, false);
|
||||
byte[] extra = out.toByteArray();
|
||||
return contentLen + extra.length;
|
||||
} catch (IOException ex) {
|
||||
// Should never happen
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
|
||||
public enum HttpMultipartMode {
|
||||
|
||||
/** RFC 822, RFC 2045, RFC 2046 compliant */
|
||||
STRICT,
|
||||
/**
|
||||
* browser-compatible mode, i.e. only write Content-Disposition; use content
|
||||
* charset
|
||||
*/
|
||||
BROWSER_COMPATIBLE
|
||||
}
|
||||
@ -1,99 +0,0 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class InputStreamBody extends AbstractContentBody {
|
||||
|
||||
private final InputStream in;
|
||||
private final String filename;
|
||||
private int contentLength;
|
||||
|
||||
public InputStreamBody(final InputStream in, final String mimeType,
|
||||
final String filename) {
|
||||
super(mimeType);
|
||||
if (in == null) {
|
||||
throw new IllegalArgumentException("Input stream may not be null");
|
||||
}
|
||||
this.in = in;
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
public InputStreamBody(final InputStream in, final String filename) {
|
||||
this(in, "application/octet-stream", filename);
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return this.in;
|
||||
}
|
||||
|
||||
public void writeTo(final OutputStream out) throws IOException {
|
||||
if (out == null) {
|
||||
throw new IllegalArgumentException("Output stream may not be null");
|
||||
}
|
||||
try {
|
||||
byte[] tmp = new byte[4096];
|
||||
int l;
|
||||
while ((l = this.in.read(tmp)) != -1) {
|
||||
out.write(tmp, 0, l);
|
||||
contentLength += l;
|
||||
}
|
||||
out.flush();
|
||||
} finally {
|
||||
this.in.close();
|
||||
}
|
||||
}
|
||||
|
||||
public String getTransferEncoding() {
|
||||
return MIME.ENC_BINARY;
|
||||
}
|
||||
|
||||
public String getCharset() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public long getContentLength() {
|
||||
try {
|
||||
return Math.max(contentLength, this.in.available());
|
||||
} catch (IOException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return this.filename;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,174 +0,0 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Random;
|
||||
|
||||
import com.foxinmy.weixin4j.http.ContentType;
|
||||
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
||||
|
||||
/**
|
||||
* Multipart/form coded HTTP entity consisting of multiple body parts.
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class MultipartEntity implements HttpEntity {
|
||||
|
||||
/**
|
||||
* The pool of ASCII chars to be used for generating a multipart boundary.
|
||||
*/
|
||||
private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
.toCharArray();
|
||||
|
||||
private final HttpMultipart multipart;
|
||||
|
||||
// @GuardedBy("dirty") // we always read dirty before accessing length
|
||||
private long length;
|
||||
private volatile boolean dirty; // used to decide whether to recalculate
|
||||
// length
|
||||
|
||||
/**
|
||||
* Creates an instance using the specified parameters
|
||||
*
|
||||
* @param mode
|
||||
* the mode to use, may be {@code null}, in which case
|
||||
* {@link HttpMultipartMode#STRICT} is used
|
||||
* @param boundary
|
||||
* the boundary string, may be {@code null}, in which case
|
||||
* {@link #generateBoundary()} is invoked to create the string
|
||||
* @param charset
|
||||
* the character set to use, may be {@code null}, in which case
|
||||
* {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
||||
*/
|
||||
public MultipartEntity(HttpMultipartMode mode, String boundary,
|
||||
Charset charset) {
|
||||
super();
|
||||
if (boundary == null) {
|
||||
boundary = generateBoundary();
|
||||
}
|
||||
if (mode == null) {
|
||||
mode = HttpMultipartMode.STRICT;
|
||||
}
|
||||
this.multipart = new HttpMultipart("form-data", charset, boundary, mode);
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance using the specified {@link HttpMultipartMode} mode.
|
||||
* Boundary and charset are set to {@code null}.
|
||||
*
|
||||
* @param mode
|
||||
* the desired mode
|
||||
*/
|
||||
public MultipartEntity(final HttpMultipartMode mode) {
|
||||
this(mode, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance using mode {@link HttpMultipartMode#STRICT}
|
||||
*/
|
||||
public MultipartEntity() {
|
||||
this(HttpMultipartMode.STRICT, null, null);
|
||||
}
|
||||
|
||||
protected String generateBoundary() {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
Random rand = new Random();
|
||||
int count = rand.nextInt(11) + 30; // a random size from 30 to 40
|
||||
for (int i = 0; i < count; i++) {
|
||||
buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public void addPart(final FormBodyPart bodyPart) {
|
||||
this.multipart.addBodyPart(bodyPart);
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
public void addPart(final String name, final ContentBody contentBody) {
|
||||
addPart(new FormBodyPart(name, contentBody));
|
||||
}
|
||||
|
||||
public boolean isRepeatable() {
|
||||
for (FormBodyPart part : this.multipart.getBodyParts()) {
|
||||
ContentBody body = part.getBody();
|
||||
if (body.getContentLength() < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isChunked() {
|
||||
return !isRepeatable();
|
||||
}
|
||||
|
||||
public boolean isStreaming() {
|
||||
return !isRepeatable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLength() {
|
||||
if (this.dirty) {
|
||||
this.length = this.multipart.getTotalLength();
|
||||
this.dirty = false;
|
||||
}
|
||||
return this.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentType getContentType() {
|
||||
return ContentType.MULTIPART_FORM_DATA;
|
||||
}
|
||||
|
||||
public void consumeContent() throws IOException,
|
||||
UnsupportedOperationException {
|
||||
if (isStreaming()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Streaming entity does not implement #consumeContent()");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getContent() throws IOException,
|
||||
UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException(
|
||||
"Multipart form entity does not implement #getContent()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(final OutputStream outstream) throws IOException {
|
||||
this.multipart.writeTo(outstream);
|
||||
outstream.flush();
|
||||
}
|
||||
}
|
||||
@ -25,7 +25,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
package com.foxinmy.weixin4j.http.apache.content;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import com.foxinmy.weixin4j.http.ContentType;
|
||||
import com.foxinmy.weixin4j.http.MimeType;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -33,36 +38,31 @@ package com.foxinmy.weixin4j.http.apache;
|
||||
*/
|
||||
public abstract class AbstractContentBody implements ContentBody {
|
||||
|
||||
private final String mimeType;
|
||||
private final String mediaType;
|
||||
private final String subType;
|
||||
private final ContentType contentType;
|
||||
|
||||
public AbstractContentBody(final String mimeType) {
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
public AbstractContentBody(final ContentType contentType) {
|
||||
super();
|
||||
if (mimeType == null) {
|
||||
throw new IllegalArgumentException("MIME type may not be null");
|
||||
}
|
||||
this.mimeType = mimeType;
|
||||
int i = mimeType.indexOf('/');
|
||||
if (i != -1) {
|
||||
this.mediaType = mimeType.substring(0, i);
|
||||
this.subType = mimeType.substring(i + 1);
|
||||
} else {
|
||||
this.mediaType = mimeType;
|
||||
this.subType = null;
|
||||
}
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
return this.mimeType;
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
public ContentType getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
public String getMediaType() {
|
||||
return this.mediaType;
|
||||
@Override
|
||||
public MimeType getMimeType() {
|
||||
return this.contentType.getMimeType();
|
||||
}
|
||||
|
||||
public String getSubType() {
|
||||
return this.subType;
|
||||
@Override
|
||||
public String getCharset() {
|
||||
final Charset charset = this.contentType.getCharset();
|
||||
return charset != null ? charset.name() : null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -24,13 +24,18 @@
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
package com.foxinmy.weixin4j.http.apache.content;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import com.foxinmy.weixin4j.http.ContentType;
|
||||
import com.foxinmy.weixin4j.http.apache.mime.MIME;
|
||||
|
||||
/**
|
||||
* Body part that is built using a byte array containing a file.
|
||||
* Binary body part backed by a byte array.
|
||||
*
|
||||
* @see org.apache.http.entity.mime.MultipartEntityBuilder
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
@ -50,14 +55,19 @@ public class ByteArrayBody extends AbstractContentBody {
|
||||
* Creates a new ByteArrayBody.
|
||||
*
|
||||
* @param data The contents of the file contained in this part.
|
||||
* @param mimeType The mime type of the file contained in this part.
|
||||
* @param mimeType The MIME type of the file contained in this part.
|
||||
* @param filename The name of the file contained in this part.
|
||||
*
|
||||
*/
|
||||
public ByteArrayBody(final byte[] data, final String mimeType, final String filename) {
|
||||
super(mimeType);
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("byte[] may not be null");
|
||||
this(data, ContentType.create(mimeType), filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
public ByteArrayBody(final byte[] data, final ContentType contentType, final String filename) {
|
||||
super(contentType);
|
||||
this.data = data;
|
||||
this.filename = filename;
|
||||
}
|
||||
@ -72,22 +82,27 @@ public class ByteArrayBody extends AbstractContentBody {
|
||||
this(data, "application/octet-stream", filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(final OutputStream out) throws IOException {
|
||||
out.write(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCharset() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTransferEncoding() {
|
||||
return MIME.ENC_BINARY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLength() {
|
||||
return data.length;
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache.content;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import com.foxinmy.weixin4j.http.MimeType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public interface ContentBody {
|
||||
/**
|
||||
* Returns the body descriptors MIME type.
|
||||
*
|
||||
* @return The MIME type, which has been parsed from the content-type
|
||||
* definition. Must not be null, but "text/plain", if no
|
||||
* content-type was specified.
|
||||
*/
|
||||
MimeType getMimeType();
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The body descriptors character set, defaulted appropriately for the MIME
|
||||
* type.
|
||||
* </p>
|
||||
* <p>
|
||||
* For {@code TEXT} types, this will be defaulted to {@code us-ascii}. For
|
||||
* other types, when the charset parameter is missing this property will be
|
||||
* null.
|
||||
* </p>
|
||||
*
|
||||
* @return Character set, which has been parsed from the content-type
|
||||
* definition. Not null for {@code TEXT} types, when unset will be
|
||||
* set to default {@code us-ascii}. For other types, when unset,
|
||||
* null will be returned.
|
||||
*/
|
||||
String getCharset();
|
||||
|
||||
/**
|
||||
* Returns the body descriptors transfer encoding.
|
||||
*
|
||||
* @return The transfer encoding. Must not be null, but "7bit", if no
|
||||
* transfer-encoding was specified.
|
||||
*/
|
||||
String getTransferEncoding();
|
||||
|
||||
/**
|
||||
* Returns the body descriptors content-length.
|
||||
*
|
||||
* @return Content length, if known, or -1, to indicate the absence of a
|
||||
* content-length header.
|
||||
*/
|
||||
long getContentLength();
|
||||
|
||||
String getFilename();
|
||||
|
||||
void writeTo(OutputStream out) throws IOException;
|
||||
}
|
||||
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache.content;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import com.foxinmy.weixin4j.http.ContentType;
|
||||
import com.foxinmy.weixin4j.http.apache.mime.MIME;
|
||||
|
||||
/**
|
||||
* Binary body part backed by a file.
|
||||
*
|
||||
* @see org.apache.http.entity.mime.MultipartEntityBuilder
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class FileBody extends AbstractContentBody {
|
||||
|
||||
private final File file;
|
||||
private final String filename;
|
||||
|
||||
/**
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
public FileBody(final File file, final String filename,
|
||||
final String mimeType, final String charset) {
|
||||
this(file, ContentType.create(mimeType, charset), filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
public FileBody(final File file, final String mimeType, final String charset) {
|
||||
this(file, null, mimeType, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public FileBody(final File file, final String mimeType) {
|
||||
this(file, ContentType.create(mimeType), null);
|
||||
}
|
||||
|
||||
public FileBody(final File file) {
|
||||
this(file, ContentType.DEFAULT_BINARY, file != null ? file.getName()
|
||||
: null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
public FileBody(final File file, final ContentType contentType,
|
||||
final String filename) {
|
||||
super(contentType);
|
||||
this.file = file;
|
||||
this.filename = filename == null ? file.getName() : filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
public FileBody(final File file, final ContentType contentType) {
|
||||
this(file, contentType, file != null ? file.getName() : null);
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new FileInputStream(this.file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(final OutputStream out) throws IOException {
|
||||
final InputStream in = new FileInputStream(this.file);
|
||||
try {
|
||||
final byte[] tmp = new byte[4096];
|
||||
int l;
|
||||
while ((l = in.read(tmp)) != -1) {
|
||||
out.write(tmp, 0, l);
|
||||
}
|
||||
out.flush();
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTransferEncoding() {
|
||||
return MIME.ENC_BINARY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLength() {
|
||||
return this.file.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return this.file;
|
||||
}
|
||||
|
||||
}
|
||||
@ -25,99 +25,86 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
package com.foxinmy.weixin4j.http.apache.content;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import com.foxinmy.weixin4j.http.ContentType;
|
||||
import com.foxinmy.weixin4j.http.apache.mime.MIME;
|
||||
|
||||
/**
|
||||
* Binary body part backed by an input stream.
|
||||
*
|
||||
* @see org.apache.http.entity.mime.MultipartEntityBuilder
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class FileBody extends AbstractContentBody {
|
||||
public class InputStreamBody extends AbstractContentBody {
|
||||
|
||||
private final File file;
|
||||
private final InputStream in;
|
||||
private final String filename;
|
||||
private final String charset;
|
||||
|
||||
/**
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
public FileBody(final File file,
|
||||
final String filename,
|
||||
final String mimeType,
|
||||
final String charset) {
|
||||
super(mimeType);
|
||||
if (file == null) {
|
||||
throw new IllegalArgumentException("File may not be null");
|
||||
public InputStreamBody(final InputStream in, final String mimeType, final String filename) {
|
||||
this(in, ContentType.create(mimeType), filename);
|
||||
}
|
||||
this.file = file;
|
||||
if (filename != null)
|
||||
|
||||
public InputStreamBody(final InputStream in, final String filename) {
|
||||
this(in, ContentType.DEFAULT_BINARY, filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
public InputStreamBody(final InputStream in, final ContentType contentType, final String filename) {
|
||||
super(contentType);
|
||||
this.in = in;
|
||||
this.filename = filename;
|
||||
else
|
||||
this.filename = file.getName();
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.1
|
||||
* @since 4.3
|
||||
*/
|
||||
public FileBody(final File file,
|
||||
final String mimeType,
|
||||
final String charset) {
|
||||
this(file, null, mimeType, charset);
|
||||
public InputStreamBody(final InputStream in, final ContentType contentType) {
|
||||
this(in, contentType, null);
|
||||
}
|
||||
|
||||
public FileBody(final File file, final String mimeType) {
|
||||
this(file, mimeType, null);
|
||||
}
|
||||
|
||||
public FileBody(final File file) {
|
||||
this(file, "application/octet-stream");
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new FileInputStream(this.file);
|
||||
public InputStream getInputStream() {
|
||||
return this.in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(final OutputStream out) throws IOException {
|
||||
if (out == null) {
|
||||
throw new IllegalArgumentException("Output stream may not be null");
|
||||
}
|
||||
InputStream in = new FileInputStream(this.file);
|
||||
try {
|
||||
byte[] tmp = new byte[4096];
|
||||
final byte[] tmp = new byte[4096];
|
||||
int l;
|
||||
while ((l = in.read(tmp)) != -1) {
|
||||
while ((l = this.in.read(tmp)) != -1) {
|
||||
out.write(tmp, 0, l);
|
||||
}
|
||||
out.flush();
|
||||
} finally {
|
||||
in.close();
|
||||
this.in.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTransferEncoding() {
|
||||
return MIME.ENC_BINARY;
|
||||
}
|
||||
|
||||
public String getCharset() {
|
||||
return charset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLength() {
|
||||
return this.file.length();
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return this.file;
|
||||
return this.filename;
|
||||
}
|
||||
|
||||
}
|
||||
@ -25,7 +25,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
package com.foxinmy.weixin4j.http.apache.content;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
@ -36,32 +36,46 @@ import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import com.foxinmy.weixin4j.http.ContentType;
|
||||
import com.foxinmy.weixin4j.http.apache.mime.MIME;
|
||||
import com.foxinmy.weixin4j.util.Consts;
|
||||
|
||||
/**
|
||||
* Text body part backed by a byte array.
|
||||
*
|
||||
* @see org.apache.http.entity.mime.MultipartEntityBuilder
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class StringBody extends AbstractContentBody {
|
||||
|
||||
private final byte[] content;
|
||||
private final Charset charset;
|
||||
|
||||
/**
|
||||
* @since 4.1
|
||||
*
|
||||
* @deprecated (4.3) use {@link StringBody#StringBody(String, ContentType)}
|
||||
* or {@link org.apache.http.entity.mime.MultipartEntityBuilder}
|
||||
*/
|
||||
@Deprecated
|
||||
public static StringBody create(
|
||||
final String text,
|
||||
final String mimeType,
|
||||
final Charset charset) throws IllegalArgumentException {
|
||||
try {
|
||||
return new StringBody(text, mimeType, charset);
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
} catch (final UnsupportedEncodingException ex) {
|
||||
throw new IllegalArgumentException("Charset " + charset + " is not supported", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.1
|
||||
*
|
||||
* @deprecated (4.3) use {@link StringBody#StringBody(String, ContentType)}
|
||||
* or {@link org.apache.http.entity.mime.MultipartEntityBuilder}
|
||||
*/
|
||||
@Deprecated
|
||||
public static StringBody create(
|
||||
final String text, final Charset charset) throws IllegalArgumentException {
|
||||
return create(text, null, charset);
|
||||
@ -69,43 +83,41 @@ public class StringBody extends AbstractContentBody {
|
||||
|
||||
/**
|
||||
* @since 4.1
|
||||
*
|
||||
* @deprecated (4.3) use {@link StringBody#StringBody(String, ContentType)}
|
||||
* or {@link org.apache.http.entity.mime.MultipartEntityBuilder}
|
||||
*/
|
||||
@Deprecated
|
||||
public static StringBody create(final String text) throws IllegalArgumentException {
|
||||
return create(text, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a StringBody from the specified text, mime type and character set.
|
||||
* Create a StringBody from the specified text, MIME type and character set.
|
||||
*
|
||||
* @param text to be used for the body, not {@code null}
|
||||
* @param mimeType the mime type, not {@code null}
|
||||
* @param mimeType the MIME type, not {@code null}
|
||||
* @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used
|
||||
* @throws UnsupportedEncodingException
|
||||
* @throws IllegalArgumentException if the {@code text} parameter is null
|
||||
*
|
||||
*/
|
||||
public StringBody(
|
||||
final String text,
|
||||
final String mimeType,
|
||||
Charset charset) throws UnsupportedEncodingException {
|
||||
super(mimeType);
|
||||
if (text == null) {
|
||||
throw new IllegalArgumentException("Text may not be null");
|
||||
}
|
||||
if (charset == null) {
|
||||
charset = Charset.forName("US-ASCII");
|
||||
}
|
||||
this.content = text.getBytes(charset.name());
|
||||
this.charset = charset;
|
||||
final Charset charset) throws UnsupportedEncodingException {
|
||||
this(text, ContentType.create(mimeType, charset != null ? charset : Consts.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a StringBody from the specified text and character set.
|
||||
* The mime type is set to "text/plain".
|
||||
* The MIME type is set to "text/plain".
|
||||
*
|
||||
* @param text to be used for the body, not {@code null}
|
||||
* @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used
|
||||
* @throws UnsupportedEncodingException
|
||||
* @throws IllegalArgumentException if the {@code text} parameter is null
|
||||
*
|
||||
*/
|
||||
public StringBody(final String text, final Charset charset) throws UnsupportedEncodingException {
|
||||
this(text, "text/plain", charset);
|
||||
@ -113,29 +125,38 @@ public class StringBody extends AbstractContentBody {
|
||||
|
||||
/**
|
||||
* Create a StringBody from the specified text.
|
||||
* The mime type is set to "text/plain".
|
||||
* The hosts default charset is used.
|
||||
* The MIME type is set to "text/plain".
|
||||
* The {@linkplain Consts#ASCII ASCII} charset is used.
|
||||
*
|
||||
* @param text to be used for the body, not {@code null}
|
||||
* @throws UnsupportedEncodingException
|
||||
* @throws IllegalArgumentException if the {@code text} parameter is null
|
||||
*
|
||||
*/
|
||||
public StringBody(final String text) throws UnsupportedEncodingException {
|
||||
this(text, "text/plain", null);
|
||||
this(text, "text/plain", Consts.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
public StringBody(final String text, final ContentType contentType) {
|
||||
super(contentType);
|
||||
final Charset charset = contentType.getCharset();
|
||||
this.content = text.getBytes(charset != null ? charset : Consts.UTF_8);
|
||||
}
|
||||
|
||||
public Reader getReader() {
|
||||
final Charset charset = getContentType().getCharset();
|
||||
return new InputStreamReader(
|
||||
new ByteArrayInputStream(this.content),
|
||||
this.charset);
|
||||
charset != null ? charset : Consts.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(final OutputStream out) throws IOException {
|
||||
if (out == null) {
|
||||
throw new IllegalArgumentException("Output stream may not be null");
|
||||
}
|
||||
InputStream in = new ByteArrayInputStream(this.content);
|
||||
byte[] tmp = new byte[4096];
|
||||
final InputStream in = new ByteArrayInputStream(this.content);
|
||||
final byte[] tmp = new byte[4096];
|
||||
int l;
|
||||
while ((l = in.read(tmp)) != -1) {
|
||||
out.write(tmp, 0, l);
|
||||
@ -143,20 +164,18 @@ public class StringBody extends AbstractContentBody {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTransferEncoding() {
|
||||
return MIME.ENC_8BIT;
|
||||
}
|
||||
|
||||
public String getCharset() {
|
||||
return this.charset.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLength() {
|
||||
return this.content.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
import com.foxinmy.weixin4j.http.apache.content.ContentBody;
|
||||
import com.foxinmy.weixin4j.util.ByteArrayBuffer;
|
||||
|
||||
/**
|
||||
* HttpMultipart represents a collection of MIME multipart encoded content bodies. This class is
|
||||
* capable of operating either in the strict (RFC 822, RFC 2045, RFC 2046 compliant) or
|
||||
* the browser compatible modes.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
abstract class AbstractMultipartForm {
|
||||
|
||||
private static ByteArrayBuffer encode(
|
||||
final Charset charset, final String string) {
|
||||
final ByteBuffer encoded = charset.encode(CharBuffer.wrap(string));
|
||||
final ByteArrayBuffer bab = new ByteArrayBuffer(encoded.remaining());
|
||||
bab.append(encoded.array(), encoded.position(), encoded.remaining());
|
||||
return bab;
|
||||
}
|
||||
|
||||
private static void writeBytes(
|
||||
final ByteArrayBuffer b, final OutputStream out) throws IOException {
|
||||
out.write(b.buffer(), 0, b.length());
|
||||
}
|
||||
|
||||
private static void writeBytes(
|
||||
final String s, final Charset charset, final OutputStream out) throws IOException {
|
||||
final ByteArrayBuffer b = encode(charset, s);
|
||||
writeBytes(b, out);
|
||||
}
|
||||
|
||||
private static void writeBytes(
|
||||
final String s, final OutputStream out) throws IOException {
|
||||
final ByteArrayBuffer b = encode(MIME.DEFAULT_CHARSET, s);
|
||||
writeBytes(b, out);
|
||||
}
|
||||
|
||||
protected static void writeField(
|
||||
final MinimalField field, final OutputStream out) throws IOException {
|
||||
writeBytes(field.getName(), out);
|
||||
writeBytes(FIELD_SEP, out);
|
||||
writeBytes(field.getBody(), out);
|
||||
writeBytes(CR_LF, out);
|
||||
}
|
||||
|
||||
protected static void writeField(
|
||||
final MinimalField field, final Charset charset, final OutputStream out) throws IOException {
|
||||
writeBytes(field.getName(), charset, out);
|
||||
writeBytes(FIELD_SEP, out);
|
||||
writeBytes(field.getBody(), charset, out);
|
||||
writeBytes(CR_LF, out);
|
||||
}
|
||||
|
||||
private static final ByteArrayBuffer FIELD_SEP = encode(MIME.DEFAULT_CHARSET, ": ");
|
||||
private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET, "\r\n");
|
||||
private static final ByteArrayBuffer TWO_DASHES = encode(MIME.DEFAULT_CHARSET, "--");
|
||||
|
||||
final Charset charset;
|
||||
final String boundary;
|
||||
|
||||
/**
|
||||
* Creates an instance with the specified settings.
|
||||
*
|
||||
* @param charset the character set to use. May be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
||||
* @param boundary to use - must not be {@code null}
|
||||
* @throws IllegalArgumentException if charset is null or boundary is null
|
||||
*/
|
||||
public AbstractMultipartForm(final Charset charset, final String boundary) {
|
||||
super();
|
||||
this.charset = charset != null ? charset : MIME.DEFAULT_CHARSET;
|
||||
this.boundary = boundary;
|
||||
}
|
||||
|
||||
public AbstractMultipartForm(final String boundary) {
|
||||
this(null, boundary);
|
||||
}
|
||||
|
||||
public abstract List<FormBodyPart> getBodyParts();
|
||||
|
||||
void doWriteTo(
|
||||
final OutputStream out,
|
||||
final boolean writeContent) throws IOException {
|
||||
|
||||
final ByteArrayBuffer boundaryEncoded = encode(this.charset, this.boundary);
|
||||
for (final FormBodyPart part: getBodyParts()) {
|
||||
writeBytes(TWO_DASHES, out);
|
||||
writeBytes(boundaryEncoded, out);
|
||||
writeBytes(CR_LF, out);
|
||||
|
||||
formatMultipartHeader(part, out);
|
||||
|
||||
writeBytes(CR_LF, out);
|
||||
|
||||
if (writeContent) {
|
||||
part.getBody().writeTo(out);
|
||||
}
|
||||
writeBytes(CR_LF, out);
|
||||
}
|
||||
writeBytes(TWO_DASHES, out);
|
||||
writeBytes(boundaryEncoded, out);
|
||||
writeBytes(TWO_DASHES, out);
|
||||
writeBytes(CR_LF, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the multipart header fields; depends on the style.
|
||||
*/
|
||||
protected abstract void formatMultipartHeader(
|
||||
final FormBodyPart part,
|
||||
final OutputStream out) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes out the content in the multipart/form encoding. This method
|
||||
* produces slightly different formatting depending on its compatibility
|
||||
* mode.
|
||||
*/
|
||||
public void writeTo(final OutputStream out) throws IOException {
|
||||
doWriteTo(out, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the total length of the multipart content (content length of
|
||||
* individual parts plus that of extra elements required to delimit the parts
|
||||
* from one another). If any of the @{link BodyPart}s contained in this object
|
||||
* is of a streaming entity of unknown length the total length is also unknown.
|
||||
* <p>
|
||||
* This method buffers only a small amount of data in order to determine the
|
||||
* total length of the entire entity. The content of individual parts is not
|
||||
* buffered.
|
||||
* </p>
|
||||
*
|
||||
* @return total length of the multipart entity if known, {@code -1}
|
||||
* otherwise.
|
||||
*/
|
||||
public long getTotalLength() {
|
||||
long contentLen = 0;
|
||||
for (final FormBodyPart part: getBodyParts()) {
|
||||
final ContentBody body = part.getBody();
|
||||
final long len = body.getContentLength();
|
||||
if (len >= 0) {
|
||||
contentLen += len;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
doWriteTo(out, false);
|
||||
final byte[] extra = out.toByteArray();
|
||||
return contentLen + extra.length;
|
||||
} catch (final IOException ex) {
|
||||
// Should never happen
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -25,10 +25,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
|
||||
import com.foxinmy.weixin4j.util.NameValue;
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
import com.foxinmy.weixin4j.http.ContentType;
|
||||
import com.foxinmy.weixin4j.http.apache.content.AbstractContentBody;
|
||||
import com.foxinmy.weixin4j.http.apache.content.ContentBody;
|
||||
|
||||
/**
|
||||
* FormBodyPart class represents a content body that can be used as a part of multipart encoded
|
||||
@ -41,17 +42,20 @@ public class FormBodyPart {
|
||||
|
||||
private final String name;
|
||||
private final Header header;
|
||||
|
||||
private final ContentBody body;
|
||||
|
||||
FormBodyPart(final String name, final ContentBody body, final Header header) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.body = body;
|
||||
this.header = header != null ? header : new Header();
|
||||
}
|
||||
|
||||
/**
|
||||
* (4.4) use {@link com.foxinmy.weixin4j.http.apache.mime.FormBodyPartBuilder}.
|
||||
*/
|
||||
public FormBodyPart(final String name, final ContentBody body) {
|
||||
super();
|
||||
if (name == null) {
|
||||
throw new IllegalArgumentException("Name may not be null");
|
||||
}
|
||||
if (body == null) {
|
||||
throw new IllegalArgumentException("Body may not be null");
|
||||
}
|
||||
this.name = name;
|
||||
this.body = body;
|
||||
this.header = new Header();
|
||||
@ -74,14 +78,14 @@ public class FormBodyPart {
|
||||
}
|
||||
|
||||
public void addField(final String name, final String value) {
|
||||
if (name == null) {
|
||||
throw new IllegalArgumentException("Field name may not be null");
|
||||
}
|
||||
this.header.addField(new NameValue(name, value));
|
||||
this.header.addField(new MinimalField(name, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* (4.4) use {@link com.foxinmy.weixin4j.http.apache.mime.FormBodyPartBuilder}.
|
||||
*/
|
||||
protected void generateContentDisp(final ContentBody body) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("form-data; name=\"");
|
||||
buffer.append(getName());
|
||||
buffer.append("\"");
|
||||
@ -93,8 +97,20 @@ public class FormBodyPart {
|
||||
addField(MIME.CONTENT_DISPOSITION, buffer.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* (4.4) use {@link com.foxinmy.weixin4j.http.apache.mime.FormBodyPartBuilder}.
|
||||
*/
|
||||
protected void generateContentType(final ContentBody body) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
final ContentType contentType;
|
||||
if (body instanceof AbstractContentBody) {
|
||||
contentType = ((AbstractContentBody) body).getContentType();
|
||||
} else {
|
||||
contentType = null;
|
||||
}
|
||||
if (contentType != null) {
|
||||
addField(MIME.CONTENT_TYPE, contentType.toString());
|
||||
} else {
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
buffer.append(body.getMimeType()); // MimeType cannot be null
|
||||
if (body.getCharset() != null) { // charset may legitimately be null
|
||||
buffer.append("; charset=");
|
||||
@ -102,9 +118,12 @@ public class FormBodyPart {
|
||||
}
|
||||
addField(MIME.CONTENT_TYPE, buffer.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (4.4) use {@link com.foxinmy.weixin4j.http.apache.mime.FormBodyPartBuilder}.
|
||||
*/
|
||||
protected void generateTransferEncoding(final ContentBody body) {
|
||||
addField(MIME.CONTENT_TRANSFER_ENC, body.getTransferEncoding()); // TE cannot be null
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.foxinmy.weixin4j.http.ContentType;
|
||||
import com.foxinmy.weixin4j.http.apache.content.AbstractContentBody;
|
||||
import com.foxinmy.weixin4j.http.apache.content.ContentBody;
|
||||
|
||||
/**
|
||||
* Builder for individual {@link com.foxinmy.weixin4j.http.apache.mime.FormBodyPart}s.
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
public class FormBodyPartBuilder {
|
||||
|
||||
private String name;
|
||||
private ContentBody body;
|
||||
private final Header header;
|
||||
|
||||
public static FormBodyPartBuilder create(final String name, final ContentBody body) {
|
||||
return new FormBodyPartBuilder(name, body);
|
||||
}
|
||||
|
||||
public static FormBodyPartBuilder create() {
|
||||
return new FormBodyPartBuilder();
|
||||
}
|
||||
|
||||
FormBodyPartBuilder(final String name, final ContentBody body) {
|
||||
this();
|
||||
this.name = name;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
FormBodyPartBuilder() {
|
||||
this.header = new Header();
|
||||
}
|
||||
|
||||
public FormBodyPartBuilder setName(final String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FormBodyPartBuilder setBody(final ContentBody body) {
|
||||
this.body = body;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FormBodyPartBuilder addField(final String name, final String value) {
|
||||
this.header.addField(new MinimalField(name, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public FormBodyPartBuilder setField(final String name, final String value) {
|
||||
this.header.setField(new MinimalField(name, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public FormBodyPartBuilder removeFields(final String name) {
|
||||
this.header.removeFields(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FormBodyPart build() {
|
||||
final Header headerCopy = new Header();
|
||||
final List<MinimalField> fields = this.header.getFields();
|
||||
for (final MinimalField field: fields) {
|
||||
headerCopy.addField(field);
|
||||
}
|
||||
if (headerCopy.getField(MIME.CONTENT_DISPOSITION) == null) {
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("form-data; name=\"");
|
||||
buffer.append(encodeForHeader(this.name));
|
||||
buffer.append("\"");
|
||||
if (this.body.getFilename() != null) {
|
||||
buffer.append("; filename=\"");
|
||||
buffer.append(encodeForHeader(this.body.getFilename()));
|
||||
buffer.append("\"");
|
||||
}
|
||||
headerCopy.addField(new MinimalField(MIME.CONTENT_DISPOSITION, buffer.toString()));
|
||||
}
|
||||
if (headerCopy.getField(MIME.CONTENT_TYPE) == null) {
|
||||
final ContentType contentType;
|
||||
if (body instanceof AbstractContentBody) {
|
||||
contentType = ((AbstractContentBody) body).getContentType();
|
||||
} else {
|
||||
contentType = null;
|
||||
}
|
||||
if (contentType != null) {
|
||||
headerCopy.addField(new MinimalField(MIME.CONTENT_TYPE, contentType.toString()));
|
||||
} else {
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
buffer.append(this.body.getMimeType()); // MimeType cannot be null
|
||||
if (this.body.getCharset() != null) { // charset may legitimately be null
|
||||
buffer.append("; charset=");
|
||||
buffer.append(this.body.getCharset());
|
||||
}
|
||||
headerCopy.addField(new MinimalField(MIME.CONTENT_TYPE, buffer.toString()));
|
||||
}
|
||||
}
|
||||
if (headerCopy.getField(MIME.CONTENT_TRANSFER_ENC) == null) {
|
||||
// TE cannot be null
|
||||
headerCopy.addField(new MinimalField(MIME.CONTENT_TRANSFER_ENC, body.getTransferEncoding()));
|
||||
}
|
||||
return new FormBodyPart(this.name, this.body, headerCopy);
|
||||
}
|
||||
|
||||
private static String encodeForHeader(final String headerName) {
|
||||
if (headerName == null) {
|
||||
return null;
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < headerName.length(); i++) {
|
||||
final char x = headerName.charAt(i);
|
||||
if (x == '"' || x == '\\' || x == '\r') {
|
||||
sb.append("\\");
|
||||
}
|
||||
sb.append(x);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The header of an entity (see RFC 2045).
|
||||
*/
|
||||
public class Header implements Iterable<MinimalField> {
|
||||
|
||||
private final List<MinimalField> fields;
|
||||
private final Map<String, List<MinimalField>> fieldMap;
|
||||
|
||||
public Header() {
|
||||
super();
|
||||
this.fields = new LinkedList<MinimalField>();
|
||||
this.fieldMap = new HashMap<String, List<MinimalField>>();
|
||||
}
|
||||
|
||||
public void addField(final MinimalField field) {
|
||||
if (field == null) {
|
||||
return;
|
||||
}
|
||||
final String key = field.getName().toLowerCase(Locale.ROOT);
|
||||
List<MinimalField> values = this.fieldMap.get(key);
|
||||
if (values == null) {
|
||||
values = new LinkedList<MinimalField>();
|
||||
this.fieldMap.put(key, values);
|
||||
}
|
||||
values.add(field);
|
||||
this.fields.add(field);
|
||||
}
|
||||
|
||||
public List<MinimalField> getFields() {
|
||||
return new ArrayList<MinimalField>(this.fields);
|
||||
}
|
||||
|
||||
public MinimalField getField(final String name) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
final String key = name.toLowerCase(Locale.ROOT);
|
||||
final List<MinimalField> list = this.fieldMap.get(key);
|
||||
if (list != null && !list.isEmpty()) {
|
||||
return list.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<MinimalField> getFields(final String name) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
final String key = name.toLowerCase(Locale.ROOT);
|
||||
final List<MinimalField> list = this.fieldMap.get(key);
|
||||
if (list == null || list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
return new ArrayList<MinimalField>(list);
|
||||
}
|
||||
}
|
||||
|
||||
public int removeFields(final String name) {
|
||||
if (name == null) {
|
||||
return 0;
|
||||
}
|
||||
final String key = name.toLowerCase(Locale.ROOT);
|
||||
final List<MinimalField> removed = fieldMap.remove(key);
|
||||
if (removed == null || removed.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
this.fields.removeAll(removed);
|
||||
return removed.size();
|
||||
}
|
||||
|
||||
public void setField(final MinimalField field) {
|
||||
if (field == null) {
|
||||
return;
|
||||
}
|
||||
final String key = field.getName().toLowerCase(Locale.ROOT);
|
||||
final List<MinimalField> list = fieldMap.get(key);
|
||||
if (list == null || list.isEmpty()) {
|
||||
addField(field);
|
||||
return;
|
||||
}
|
||||
list.clear();
|
||||
list.add(field);
|
||||
int firstOccurrence = -1;
|
||||
int index = 0;
|
||||
for (final Iterator<MinimalField> it = this.fields.iterator(); it.hasNext(); index++) {
|
||||
final MinimalField f = it.next();
|
||||
if (f.getName().equalsIgnoreCase(field.getName())) {
|
||||
it.remove();
|
||||
if (firstOccurrence == -1) {
|
||||
firstOccurrence = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.fields.add(firstOccurrence, field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<MinimalField> iterator() {
|
||||
return Collections.unmodifiableList(fields).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.fields.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* HttpBrowserCompatibleMultipart represents a collection of MIME multipart encoded
|
||||
* content bodies. This class is emulates browser compatibility, e.g. IE 5 or earlier.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
class HttpBrowserCompatibleMultipart extends AbstractMultipartForm {
|
||||
|
||||
private final List<FormBodyPart> parts;
|
||||
|
||||
public HttpBrowserCompatibleMultipart(
|
||||
final Charset charset,
|
||||
final String boundary,
|
||||
final List<FormBodyPart> parts) {
|
||||
super(charset, boundary);
|
||||
this.parts = parts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FormBodyPart> getBodyParts() {
|
||||
return this.parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the multipart header fields; depends on the style.
|
||||
*/
|
||||
@Override
|
||||
protected void formatMultipartHeader(
|
||||
final FormBodyPart part,
|
||||
final OutputStream out) throws IOException {
|
||||
// For browser-compatible, only write Content-Disposition
|
||||
// Use content charset
|
||||
final Header header = part.getHeader();
|
||||
final MinimalField cd = header.getField(MIME.CONTENT_DISPOSITION);
|
||||
writeField(cd, this.charset, out);
|
||||
final String filename = part.getBody().getFilename();
|
||||
if (filename != null) {
|
||||
final MinimalField ct = header.getField(MIME.CONTENT_TYPE);
|
||||
writeField(ct, this.charset, out);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* HttpMultipart represents a collection of MIME multipart encoded content bodies. This class is
|
||||
* capable of operating either in the strict (RFC 822, RFC 2045, RFC 2046 compliant) or
|
||||
* the browser compatible modes.
|
||||
*
|
||||
* @since 4.0
|
||||
*
|
||||
* @deprecated (4.3) Use {@link MultipartEntityBuilder}.
|
||||
*/
|
||||
@Deprecated
|
||||
public class HttpMultipart extends AbstractMultipartForm {
|
||||
|
||||
private final HttpMultipartMode mode;
|
||||
private final List<FormBodyPart> parts;
|
||||
|
||||
private final String subType;
|
||||
|
||||
/**
|
||||
* Creates an instance with the specified settings.
|
||||
*
|
||||
* @param subType MIME subtype - must not be {@code null}
|
||||
* @param charset the character set to use. May be {@code null},
|
||||
* in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
||||
* @param boundary to use - must not be {@code null}
|
||||
* @param mode the mode to use
|
||||
* @throws IllegalArgumentException if charset is null or boundary is null
|
||||
*/
|
||||
public HttpMultipart(
|
||||
final String subType, final Charset charset, final String boundary,
|
||||
final HttpMultipartMode mode) {
|
||||
super(charset, boundary);
|
||||
this.subType = subType;
|
||||
this.mode = mode;
|
||||
this.parts = new ArrayList<FormBodyPart>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with the specified settings.
|
||||
* Mode is set to {@link HttpMultipartMode#STRICT}
|
||||
*
|
||||
* @param subType MIME subtype - must not be {@code null}
|
||||
* @param charset the character set to use. May be {@code null},
|
||||
* in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
||||
* @param boundary to use - must not be {@code null}
|
||||
* @throws IllegalArgumentException if charset is null or boundary is null
|
||||
*/
|
||||
public HttpMultipart(final String subType, final Charset charset, final String boundary) {
|
||||
this(subType, charset, boundary, HttpMultipartMode.STRICT);
|
||||
}
|
||||
|
||||
public HttpMultipart(final String subType, final String boundary) {
|
||||
this(subType, null, boundary);
|
||||
}
|
||||
|
||||
public HttpMultipartMode getMode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void formatMultipartHeader(
|
||||
final FormBodyPart part, final OutputStream out) throws IOException {
|
||||
final Header header = part.getHeader();
|
||||
switch (this.mode) {
|
||||
case BROWSER_COMPATIBLE:
|
||||
// For browser-compatible, only write Content-Disposition
|
||||
// Use content charset
|
||||
final MinimalField cd = header.getField(MIME.CONTENT_DISPOSITION);
|
||||
writeField(cd, this.charset, out);
|
||||
final String filename = part.getBody().getFilename();
|
||||
if (filename != null) {
|
||||
final MinimalField ct = header.getField(MIME.CONTENT_TYPE);
|
||||
writeField(ct, this.charset, out);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
for (final MinimalField field: header) {
|
||||
writeField(field, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FormBodyPart> getBodyParts() {
|
||||
return this.parts;
|
||||
}
|
||||
|
||||
public void addBodyPart(final FormBodyPart part) {
|
||||
if (part == null) {
|
||||
return;
|
||||
}
|
||||
this.parts.add(part);
|
||||
}
|
||||
|
||||
public String getSubType() {
|
||||
return this.subType;
|
||||
}
|
||||
|
||||
public Charset getCharset() {
|
||||
return this.charset;
|
||||
}
|
||||
|
||||
public String getBoundary() {
|
||||
return this.boundary;
|
||||
}
|
||||
|
||||
}
|
||||
@ -25,19 +25,19 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
/**
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public interface ContentBody extends ContentDescriptor {
|
||||
public enum HttpMultipartMode {
|
||||
|
||||
String getFilename();
|
||||
|
||||
void writeTo(OutputStream out) throws IOException;
|
||||
/** RFC 822, RFC 2045, RFC 2046 compliant */
|
||||
STRICT,
|
||||
/** browser-compatible mode, i.e. only write Content-Disposition; use content charset */
|
||||
BROWSER_COMPATIBLE,
|
||||
/** RFC 6532 compliant */
|
||||
RFC6532
|
||||
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* HttpRFC6532Multipart represents a collection of MIME multipart encoded content bodies,
|
||||
* implementing the strict (RFC 822, RFC 2045, RFC 2046 compliant) interpretation
|
||||
* of the spec, with the exception of allowing UTF-8 headers, as per RFC6532.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
class HttpRFC6532Multipart extends AbstractMultipartForm {
|
||||
|
||||
private final List<FormBodyPart> parts;
|
||||
|
||||
public HttpRFC6532Multipart(
|
||||
final Charset charset,
|
||||
final String boundary,
|
||||
final List<FormBodyPart> parts) {
|
||||
super(charset, boundary);
|
||||
this.parts = parts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FormBodyPart> getBodyParts() {
|
||||
return this.parts;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void formatMultipartHeader(
|
||||
final FormBodyPart part,
|
||||
final OutputStream out) throws IOException {
|
||||
|
||||
// For RFC6532, we output all fields with UTF-8 encoding.
|
||||
final Header header = part.getHeader();
|
||||
for (final MinimalField field: header) {
|
||||
writeField(field, MIME.UTF8_CHARSET, out);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* HttpStrictMultipart represents a collection of MIME multipart encoded content bodies,
|
||||
* implementing the strict (RFC 822, RFC 2045, RFC 2046 compliant) interpretation
|
||||
* of the spec.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
class HttpStrictMultipart extends AbstractMultipartForm {
|
||||
|
||||
private final List<FormBodyPart> parts;
|
||||
|
||||
public HttpStrictMultipart(
|
||||
final Charset charset,
|
||||
final String boundary,
|
||||
final List<FormBodyPart> parts) {
|
||||
super(charset, boundary);
|
||||
this.parts = parts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FormBodyPart> getBodyParts() {
|
||||
return this.parts;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void formatMultipartHeader(
|
||||
final FormBodyPart part,
|
||||
final OutputStream out) throws IOException {
|
||||
|
||||
// For strict, we output all fields with MIME-standard encoding.
|
||||
final Header header = part.getHeader();
|
||||
for (final MinimalField field: header) {
|
||||
writeField(field, out);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -25,10 +25,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import com.foxinmy.weixin4j.util.Consts;
|
||||
|
||||
/**
|
||||
*
|
||||
* @since 4.0
|
||||
@ -42,7 +44,8 @@ public final class MIME {
|
||||
public static final String ENC_8BIT = "8bit";
|
||||
public static final String ENC_BINARY = "binary";
|
||||
|
||||
/** The default character set to be used, i.e. "US-ASCII" */
|
||||
public static final Charset DEFAULT_CHARSET = Charset.forName("US-ASCII");
|
||||
public static final Charset DEFAULT_CHARSET = Consts.UTF_8;
|
||||
|
||||
public static final Charset UTF8_CHARSET = Consts.UTF_8;
|
||||
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
/**
|
||||
* Minimal MIME field.
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class MinimalField {
|
||||
|
||||
private final String name;
|
||||
private final String value;
|
||||
|
||||
public MinimalField(final String name, final String value) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
buffer.append(this.name);
|
||||
buffer.append(": ");
|
||||
buffer.append(this.value);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Random;
|
||||
|
||||
import com.foxinmy.weixin4j.http.ContentType;
|
||||
import com.foxinmy.weixin4j.http.apache.content.ContentBody;
|
||||
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
||||
|
||||
/**
|
||||
* Multipart/form coded HTTP entity consisting of multiple body parts.
|
||||
*
|
||||
* @since 4.0
|
||||
*
|
||||
*/
|
||||
public class MultipartEntity implements HttpEntity {
|
||||
|
||||
/**
|
||||
* The pool of ASCII chars to be used for generating a multipart boundary.
|
||||
*/
|
||||
private final static char[] MULTIPART_CHARS =
|
||||
"-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
.toCharArray();
|
||||
|
||||
private final MultipartEntityBuilder builder;
|
||||
private volatile MultipartFormEntity entity;
|
||||
|
||||
/**
|
||||
* Creates an instance using the specified parameters
|
||||
* @param mode the mode to use, may be {@code null}, in which case {@link HttpMultipartMode#STRICT} is used
|
||||
* @param boundary the boundary string, may be {@code null}, in which case {@link #generateBoundary()} is invoked to create the string
|
||||
* @param charset the character set to use, may be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
||||
*/
|
||||
public MultipartEntity(
|
||||
final HttpMultipartMode mode,
|
||||
final String boundary,
|
||||
final Charset charset) {
|
||||
super();
|
||||
this.builder = new MultipartEntityBuilder()
|
||||
.setMode(mode)
|
||||
.setCharset(charset != null ? charset : MIME.DEFAULT_CHARSET)
|
||||
.setBoundary(boundary);
|
||||
this.entity = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance using the specified {@link HttpMultipartMode} mode.
|
||||
* Boundary and charset are set to {@code null}.
|
||||
* @param mode the desired mode
|
||||
*/
|
||||
public MultipartEntity(final HttpMultipartMode mode) {
|
||||
this(mode, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance using mode {@link HttpMultipartMode#STRICT}
|
||||
*/
|
||||
public MultipartEntity() {
|
||||
this(HttpMultipartMode.STRICT, null, null);
|
||||
}
|
||||
|
||||
protected String generateContentType(
|
||||
final String boundary,
|
||||
final Charset charset) {
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("multipart/form-data; boundary=");
|
||||
buffer.append(boundary);
|
||||
if (charset != null) {
|
||||
buffer.append("; charset=");
|
||||
buffer.append(charset.name());
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
protected String generateBoundary() {
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
final Random rand = new Random();
|
||||
final int count = rand.nextInt(11) + 30; // a random size from 30 to 40
|
||||
for (int i = 0; i < count; i++) {
|
||||
buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private MultipartFormEntity getEntity() {
|
||||
if (this.entity == null) {
|
||||
this.entity = this.builder.buildEntity();
|
||||
}
|
||||
return this.entity;
|
||||
}
|
||||
|
||||
public void addPart(final FormBodyPart bodyPart) {
|
||||
this.builder.addPart(bodyPart);
|
||||
this.entity = null;
|
||||
}
|
||||
|
||||
public void addPart(final String name, final ContentBody contentBody) {
|
||||
addPart(new FormBodyPart(name, contentBody));
|
||||
}
|
||||
|
||||
public boolean isRepeatable() {
|
||||
return getEntity().isRepeatable();
|
||||
}
|
||||
|
||||
public boolean isChunked() {
|
||||
return getEntity().isChunked();
|
||||
}
|
||||
|
||||
public boolean isStreaming() {
|
||||
return getEntity().isStreaming();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLength() {
|
||||
return getEntity().getContentLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentType getContentType() {
|
||||
return getEntity().getContentType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getContent() throws IOException, UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException(
|
||||
"Multipart form entity does not implement #getContent()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(final OutputStream outstream) throws IOException {
|
||||
getEntity().writeTo(outstream);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import com.foxinmy.weixin4j.http.ContentType;
|
||||
import com.foxinmy.weixin4j.http.apache.content.ByteArrayBody;
|
||||
import com.foxinmy.weixin4j.http.apache.content.ContentBody;
|
||||
import com.foxinmy.weixin4j.http.apache.content.FileBody;
|
||||
import com.foxinmy.weixin4j.http.apache.content.InputStreamBody;
|
||||
import com.foxinmy.weixin4j.http.apache.content.StringBody;
|
||||
import com.foxinmy.weixin4j.util.NameValue;
|
||||
|
||||
/**
|
||||
* Builder for multipart {@link HttpEntity}s.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
public class MultipartEntityBuilder {
|
||||
|
||||
/**
|
||||
* The pool of ASCII chars to be used for generating a multipart boundary.
|
||||
*/
|
||||
private final static char[] MULTIPART_CHARS =
|
||||
"-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
.toCharArray();
|
||||
|
||||
private final static String DEFAULT_SUBTYPE = "form-data";
|
||||
|
||||
private ContentType contentType;
|
||||
private HttpMultipartMode mode = HttpMultipartMode.STRICT;
|
||||
private String boundary = null;
|
||||
private Charset charset = null;
|
||||
private List<FormBodyPart> bodyParts = null;
|
||||
|
||||
public static MultipartEntityBuilder create() {
|
||||
return new MultipartEntityBuilder();
|
||||
}
|
||||
|
||||
MultipartEntityBuilder() {
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder setMode(final HttpMultipartMode mode) {
|
||||
this.mode = mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder setLaxMode() {
|
||||
this.mode = HttpMultipartMode.BROWSER_COMPATIBLE;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder setStrictMode() {
|
||||
this.mode = HttpMultipartMode.STRICT;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder setBoundary(final String boundary) {
|
||||
this.boundary = boundary;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.4
|
||||
*/
|
||||
public MultipartEntityBuilder setMimeSubtype(final String subType) {
|
||||
this.contentType = ContentType.create("multipart/" + subType);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.4
|
||||
*
|
||||
* @deprecated (4.5) Use {@link #setContentType(org.apache.http.entity.ContentType)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public MultipartEntityBuilder seContentType(final ContentType contentType) {
|
||||
return setContentType(contentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.5
|
||||
*/
|
||||
public MultipartEntityBuilder setContentType(final ContentType contentType) {
|
||||
this.contentType = contentType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder setCharset(final Charset charset) {
|
||||
this.charset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.4
|
||||
*/
|
||||
public MultipartEntityBuilder addPart(final FormBodyPart bodyPart) {
|
||||
if (bodyPart == null) {
|
||||
return this;
|
||||
}
|
||||
if (this.bodyParts == null) {
|
||||
this.bodyParts = new ArrayList<FormBodyPart>();
|
||||
}
|
||||
this.bodyParts.add(bodyPart);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder addPart(final String name, final ContentBody contentBody) {
|
||||
return addPart(FormBodyPartBuilder.create(name, contentBody).build());
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder addTextBody(
|
||||
final String name, final String text, final ContentType contentType) {
|
||||
return addPart(name, new StringBody(text, contentType));
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder addTextBody(
|
||||
final String name, final String text) {
|
||||
return addTextBody(name, text, ContentType.DEFAULT_TEXT);
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder addBinaryBody(
|
||||
final String name, final byte[] b, final ContentType contentType, final String filename) {
|
||||
return addPart(name, new ByteArrayBody(b, contentType, filename));
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder addBinaryBody(
|
||||
final String name, final byte[] b) {
|
||||
return addBinaryBody(name, b, ContentType.DEFAULT_BINARY, null);
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder addBinaryBody(
|
||||
final String name, final File file, final ContentType contentType, final String filename) {
|
||||
return addPart(name, new FileBody(file, contentType, filename));
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder addBinaryBody(
|
||||
final String name, final File file) {
|
||||
return addBinaryBody(name, file, ContentType.DEFAULT_BINARY, file != null ? file.getName() : null);
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder addBinaryBody(
|
||||
final String name, final InputStream stream, final ContentType contentType,
|
||||
final String filename) {
|
||||
return addPart(name, new InputStreamBody(stream, contentType, filename));
|
||||
}
|
||||
|
||||
public MultipartEntityBuilder addBinaryBody(final String name, final InputStream stream) {
|
||||
return addBinaryBody(name, stream, ContentType.DEFAULT_BINARY, null);
|
||||
}
|
||||
|
||||
private String generateBoundary() {
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
final Random rand = new Random();
|
||||
final int count = rand.nextInt(11) + 30; // a random size from 30 to 40
|
||||
for (int i = 0; i < count; i++) {
|
||||
buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public MultipartFormEntity buildEntity() {
|
||||
String boundaryCopy = boundary;
|
||||
if (boundaryCopy == null && contentType != null) {
|
||||
boundaryCopy = contentType.getParameter("boundary");
|
||||
}
|
||||
if (boundaryCopy == null) {
|
||||
boundaryCopy = generateBoundary();
|
||||
}
|
||||
Charset charsetCopy = charset;
|
||||
if (charsetCopy == null && contentType != null) {
|
||||
charsetCopy = contentType.getCharset();
|
||||
}
|
||||
final List<NameValue> paramsList = new ArrayList<NameValue>(2);
|
||||
paramsList.add(new NameValue("boundary", boundaryCopy));
|
||||
if (charsetCopy != null) {
|
||||
paramsList.add(new NameValue("charset", charsetCopy.name()));
|
||||
}
|
||||
final NameValue[] params = paramsList.toArray(new NameValue[paramsList.size()]);
|
||||
final ContentType contentTypeCopy = contentType != null ?
|
||||
contentType.withParameters(params) :
|
||||
ContentType.create("multipart/" + DEFAULT_SUBTYPE, params);
|
||||
final List<FormBodyPart> bodyPartsCopy = bodyParts != null ? new ArrayList<FormBodyPart>(bodyParts) :
|
||||
Collections.<FormBodyPart>emptyList();
|
||||
final HttpMultipartMode modeCopy = mode != null ? mode : HttpMultipartMode.STRICT;
|
||||
final AbstractMultipartForm form;
|
||||
switch (modeCopy) {
|
||||
case BROWSER_COMPATIBLE:
|
||||
form = new HttpBrowserCompatibleMultipart(charsetCopy, boundaryCopy, bodyPartsCopy);
|
||||
break;
|
||||
case RFC6532:
|
||||
form = new HttpRFC6532Multipart(charsetCopy, boundaryCopy, bodyPartsCopy);
|
||||
break;
|
||||
default:
|
||||
form = new HttpStrictMultipart(charsetCopy, boundaryCopy, bodyPartsCopy);
|
||||
}
|
||||
return new MultipartFormEntity(form, contentTypeCopy, form.getTotalLength());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache.mime;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import com.foxinmy.weixin4j.http.ContentType;
|
||||
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
||||
|
||||
class MultipartFormEntity implements HttpEntity {
|
||||
|
||||
private final AbstractMultipartForm multipart;
|
||||
private final ContentType contentType;
|
||||
private final long contentLength;
|
||||
|
||||
MultipartFormEntity(
|
||||
final AbstractMultipartForm multipart,
|
||||
final ContentType contentType,
|
||||
final long contentLength) {
|
||||
super();
|
||||
this.multipart = multipart;
|
||||
this.contentType = contentType;
|
||||
this.contentLength = contentLength;
|
||||
}
|
||||
|
||||
AbstractMultipartForm getMultipart() {
|
||||
return this.multipart;
|
||||
}
|
||||
|
||||
public boolean isRepeatable() {
|
||||
return this.contentLength != -1;
|
||||
}
|
||||
|
||||
public boolean isChunked() {
|
||||
return !isRepeatable();
|
||||
}
|
||||
|
||||
public boolean isStreaming() {
|
||||
return !isRepeatable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLength() {
|
||||
return this.contentLength;
|
||||
}
|
||||
|
||||
public ContentType getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getContent() throws IOException {
|
||||
if (this.contentLength < 0) {
|
||||
throw new IllegalArgumentException("Content length is unknown");
|
||||
} else if (this.contentLength > 25 * 1024) {
|
||||
throw new IllegalArgumentException("Content length is too long: " + this.contentLength);
|
||||
}
|
||||
final ByteArrayOutputStream outstream = new ByteArrayOutputStream();
|
||||
writeTo(outstream);
|
||||
outstream.flush();
|
||||
return new ByteArrayInputStream(outstream.toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(final OutputStream outstream) throws IOException {
|
||||
this.multipart.writeTo(outstream);
|
||||
}
|
||||
}
|
||||
@ -33,7 +33,7 @@ import com.foxinmy.weixin4j.http.HttpHeaders;
|
||||
import com.foxinmy.weixin4j.http.HttpMethod;
|
||||
import com.foxinmy.weixin4j.http.HttpRequest;
|
||||
import com.foxinmy.weixin4j.http.HttpResponse;
|
||||
import com.foxinmy.weixin4j.http.apache.MultipartEntity;
|
||||
import com.foxinmy.weixin4j.http.apache.mime.MultipartEntity;
|
||||
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
||||
import com.foxinmy.weixin4j.util.Consts;
|
||||
import com.foxinmy.weixin4j.util.StringUtil;
|
||||
|
||||
@ -31,7 +31,7 @@ import com.foxinmy.weixin4j.http.HttpClientException;
|
||||
import com.foxinmy.weixin4j.http.HttpHeaders;
|
||||
import com.foxinmy.weixin4j.http.HttpMethod;
|
||||
import com.foxinmy.weixin4j.http.HttpRequest;
|
||||
import com.foxinmy.weixin4j.http.apache.MultipartEntity;
|
||||
import com.foxinmy.weixin4j.http.apache.mime.MultipartEntity;
|
||||
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
||||
import com.foxinmy.weixin4j.util.StringUtil;
|
||||
|
||||
|
||||
@ -17,9 +17,9 @@ import com.foxinmy.weixin4j.http.HttpRequest;
|
||||
import com.foxinmy.weixin4j.http.HttpResponse;
|
||||
import com.foxinmy.weixin4j.http.MimeType;
|
||||
import com.foxinmy.weixin4j.http.URLParameter;
|
||||
import com.foxinmy.weixin4j.http.apache.FormBodyPart;
|
||||
import com.foxinmy.weixin4j.http.apache.HttpMultipartMode;
|
||||
import com.foxinmy.weixin4j.http.apache.MultipartEntity;
|
||||
import com.foxinmy.weixin4j.http.apache.mime.FormBodyPart;
|
||||
import com.foxinmy.weixin4j.http.apache.mime.HttpMultipartMode;
|
||||
import com.foxinmy.weixin4j.http.apache.mime.MultipartEntityBuilder;
|
||||
import com.foxinmy.weixin4j.http.entity.FormUrlEntity;
|
||||
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
||||
import com.foxinmy.weixin4j.http.entity.StringEntity;
|
||||
@ -85,13 +85,13 @@ public class WeixinRequestExecutor {
|
||||
*/
|
||||
public WeixinResponse post(String url, FormBodyPart... bodyParts)
|
||||
throws WeixinException {
|
||||
MultipartEntity entity = new MultipartEntity(
|
||||
HttpMultipartMode.BROWSER_COMPATIBLE, null, Consts.UTF_8);
|
||||
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
|
||||
for (FormBodyPart bodyPart : bodyParts) {
|
||||
entity.addPart(bodyPart);
|
||||
builder.addPart(bodyPart);
|
||||
}
|
||||
HttpRequest request = new HttpRequest(HttpMethod.POST, url);
|
||||
request.setEntity(entity);
|
||||
request.setEntity(builder.setMode(HttpMultipartMode.RFC6532)
|
||||
.buildEntity());
|
||||
return doRequest(request);
|
||||
}
|
||||
|
||||
|
||||
@ -25,11 +25,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
package com.foxinmy.weixin4j.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* A resizable byte array.
|
||||
*
|
||||
@ -48,33 +47,30 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
*
|
||||
* @param capacity the capacity
|
||||
*/
|
||||
public ByteArrayBuffer(int capacity) {
|
||||
public ByteArrayBuffer(final int capacity) {
|
||||
super();
|
||||
if (capacity < 0) {
|
||||
throw new IllegalArgumentException("Buffer capacity may not be negative");
|
||||
}
|
||||
this.buffer = new byte[capacity];
|
||||
}
|
||||
|
||||
private void expand(int newlen) {
|
||||
byte newbuffer[] = new byte[Math.max(this.buffer.length << 1, newlen)];
|
||||
private void expand(final int newlen) {
|
||||
final byte newbuffer[] = new byte[Math.max(this.buffer.length << 1, newlen)];
|
||||
System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
|
||||
this.buffer = newbuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends <code>len</code> bytes to this buffer from the given source
|
||||
* array starting at index <code>off</code>. The capacity of the buffer
|
||||
* is increased, if necessary, to accommodate all <code>len</code> bytes.
|
||||
* Appends {@code len} bytes to this buffer from the given source
|
||||
* array starting at index {@code off}. The capacity of the buffer
|
||||
* is increased, if necessary, to accommodate all {@code len} bytes.
|
||||
*
|
||||
* @param b the bytes to be appended.
|
||||
* @param off the index of the first byte to append.
|
||||
* @param len the number of bytes to append.
|
||||
* @throws IndexOutOfBoundsException if <code>off</code> if out of
|
||||
* range, <code>len</code> is negative, or
|
||||
* <code>off</code> + <code>len</code> is out of range.
|
||||
* @throws IndexOutOfBoundsException if {@code off} if out of
|
||||
* range, {@code len} is negative, or
|
||||
* {@code off} + {@code len} is out of range.
|
||||
*/
|
||||
public void append(final byte[] b, int off, int len) {
|
||||
public void append(final byte[] b, final int off, final int len) {
|
||||
if (b == null) {
|
||||
return;
|
||||
}
|
||||
@ -85,7 +81,7 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
int newlen = this.len + len;
|
||||
final int newlen = this.len + len;
|
||||
if (newlen > this.buffer.length) {
|
||||
expand(newlen);
|
||||
}
|
||||
@ -94,13 +90,13 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends <code>b</code> byte to this buffer. The capacity of the buffer
|
||||
* Appends {@code b} byte to this buffer. The capacity of the buffer
|
||||
* is increased, if necessary, to accommodate the additional byte.
|
||||
*
|
||||
* @param b the byte to be appended.
|
||||
*/
|
||||
public void append(int b) {
|
||||
int newlen = this.len + 1;
|
||||
public void append(final int b) {
|
||||
final int newlen = this.len + 1;
|
||||
if (newlen > this.buffer.length) {
|
||||
expand(newlen);
|
||||
}
|
||||
@ -109,20 +105,20 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends <code>len</code> chars to this buffer from the given source
|
||||
* array starting at index <code>off</code>. The capacity of the buffer
|
||||
* is increased if necessary to accommodate all <code>len</code> chars.
|
||||
* Appends {@code len} chars to this buffer from the given source
|
||||
* array starting at index {@code off}. The capacity of the buffer
|
||||
* is increased if necessary to accommodate all {@code len} chars.
|
||||
* <p>
|
||||
* The chars are converted to bytes using simple cast.
|
||||
*
|
||||
* @param b the chars to be appended.
|
||||
* @param off the index of the first char to append.
|
||||
* @param len the number of bytes to append.
|
||||
* @throws IndexOutOfBoundsException if <code>off</code> if out of
|
||||
* range, <code>len</code> is negative, or
|
||||
* <code>off</code> + <code>len</code> is out of range.
|
||||
* @throws IndexOutOfBoundsException if {@code off} if out of
|
||||
* range, {@code len} is negative, or
|
||||
* {@code off} + {@code len} is out of range.
|
||||
*/
|
||||
public void append(final char[] b, int off, int len) {
|
||||
public void append(final char[] b, final int off, final int len) {
|
||||
if (b == null) {
|
||||
return;
|
||||
}
|
||||
@ -133,8 +129,8 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
int oldlen = this.len;
|
||||
int newlen = oldlen + len;
|
||||
final int oldlen = this.len;
|
||||
final int newlen = oldlen + len;
|
||||
if (newlen > this.buffer.length) {
|
||||
expand(newlen);
|
||||
}
|
||||
@ -145,21 +141,21 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends <code>len</code> chars to this buffer from the given source
|
||||
* char array buffer starting at index <code>off</code>. The capacity
|
||||
* Appends {@code len} chars to this buffer from the given source
|
||||
* char array buffer starting at index {@code off}. The capacity
|
||||
* of the buffer is increased if necessary to accommodate all
|
||||
* <code>len</code> chars.
|
||||
* {@code len} chars.
|
||||
* <p>
|
||||
* The chars are converted to bytes using simple cast.
|
||||
*
|
||||
* @param b the chars to be appended.
|
||||
* @param off the index of the first char to append.
|
||||
* @param len the number of bytes to append.
|
||||
* @throws IndexOutOfBoundsException if <code>off</code> if out of
|
||||
* range, <code>len</code> is negative, or
|
||||
* <code>off</code> + <code>len</code> is out of range.
|
||||
* @throws IndexOutOfBoundsException if {@code off} if out of
|
||||
* range, {@code len} is negative, or
|
||||
* {@code off} + {@code len} is out of range.
|
||||
*/
|
||||
public void append(final CharArrayBuffer b, int off, int len) {
|
||||
public void append(final CharArrayBuffer b, final int off, final int len) {
|
||||
if (b == null) {
|
||||
return;
|
||||
}
|
||||
@ -179,7 +175,7 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
* @return byte array
|
||||
*/
|
||||
public byte[] toByteArray() {
|
||||
byte[] b = new byte[this.len];
|
||||
final byte[] b = new byte[this.len];
|
||||
if (this.len > 0) {
|
||||
System.arraycopy(this.buffer, 0, b, 0, this.len);
|
||||
}
|
||||
@ -187,16 +183,16 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>byte</code> value in this buffer at the specified
|
||||
* Returns the {@code byte} value in this buffer at the specified
|
||||
* index. The index argument must be greater than or equal to
|
||||
* <code>0</code>, and less than the length of this buffer.
|
||||
* {@code 0}, and less than the length of this buffer.
|
||||
*
|
||||
* @param i the index of the desired byte value.
|
||||
* @return the byte value at the specified index.
|
||||
* @throws IndexOutOfBoundsException if <code>index</code> is
|
||||
* @throws IndexOutOfBoundsException if {@code index} is
|
||||
* negative or greater than or equal to {@link #length()}.
|
||||
*/
|
||||
public int byteAt(int i) {
|
||||
public int byteAt(final int i) {
|
||||
return this.buffer[i];
|
||||
}
|
||||
|
||||
@ -223,18 +219,18 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
/**
|
||||
* Ensures that the capacity is at least equal to the specified minimum.
|
||||
* If the current capacity is less than the argument, then a new internal
|
||||
* array is allocated with greater capacity. If the <code>required</code>
|
||||
* array is allocated with greater capacity. If the {@code required}
|
||||
* argument is non-positive, this method takes no action.
|
||||
*
|
||||
* @param required the minimum required capacity.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public void ensureCapacity(int required) {
|
||||
public void ensureCapacity(final int required) {
|
||||
if (required <= 0) {
|
||||
return;
|
||||
}
|
||||
int available = this.buffer.length - this.len;
|
||||
final int available = this.buffer.length - this.len;
|
||||
if (required > available) {
|
||||
expand(this.len + required);
|
||||
}
|
||||
@ -252,14 +248,14 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
/**
|
||||
* Sets the length of the buffer. The new length value is expected to be
|
||||
* less than the current capacity and greater than or equal to
|
||||
* <code>0</code>.
|
||||
* {@code 0}.
|
||||
*
|
||||
* @param len the new length
|
||||
* @throws IndexOutOfBoundsException if the
|
||||
* <code>len</code> argument is greater than the current
|
||||
* capacity of the buffer or less than <code>0</code>.
|
||||
* {@code len} argument is greater than the current
|
||||
* capacity of the buffer or less than {@code 0}.
|
||||
*/
|
||||
public void setLength(int len) {
|
||||
public void setLength(final int len) {
|
||||
if (len < 0 || len > this.buffer.length) {
|
||||
throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.buffer.length);
|
||||
}
|
||||
@ -267,9 +263,9 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if this buffer is empty, that is, its
|
||||
* {@link #length()} is equal to <code>0</code>.
|
||||
* @return <code>true</code> if this buffer is empty, <code>false</code>
|
||||
* Returns {@code true} if this buffer is empty, that is, its
|
||||
* {@link #length()} is equal to {@code 0}.
|
||||
* @return {@code true} if this buffer is empty, {@code false}
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
@ -277,9 +273,9 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if this buffer is full, that is, its
|
||||
* Returns {@code true} if this buffer is full, that is, its
|
||||
* {@link #length()} is equal to its {@link #capacity()}.
|
||||
* @return <code>true</code> if this buffer is full, <code>false</code>
|
||||
* @return {@code true} if this buffer is full, {@code false}
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isFull() {
|
||||
@ -289,30 +285,32 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
/**
|
||||
* Returns the index within this buffer of the first occurrence of the
|
||||
* specified byte, starting the search at the specified
|
||||
* <code>beginIndex</code> and finishing at <code>endIndex</code>.
|
||||
* {@code beginIndex} and finishing at {@code endIndex}.
|
||||
* If no such byte occurs in this buffer within the specified bounds,
|
||||
* <code>-1</code> is returned.
|
||||
* {@code -1} is returned.
|
||||
* <p>
|
||||
* There is no restriction on the value of <code>beginIndex</code> and
|
||||
* <code>endIndex</code>. If <code>beginIndex</code> is negative,
|
||||
* it has the same effect as if it were zero. If <code>endIndex</code> is
|
||||
* There is no restriction on the value of {@code beginIndex} and
|
||||
* {@code endIndex}. If {@code beginIndex} is negative,
|
||||
* it has the same effect as if it were zero. If {@code endIndex} is
|
||||
* greater than {@link #length()}, it has the same effect as if it were
|
||||
* {@link #length()}. If the <code>beginIndex</code> is greater than
|
||||
* the <code>endIndex</code>, <code>-1</code> is returned.
|
||||
* {@link #length()}. If the {@code beginIndex} is greater than
|
||||
* the {@code endIndex}, {@code -1} is returned.
|
||||
*
|
||||
* @param b the byte to search for.
|
||||
* @param beginIndex the index to start the search from.
|
||||
* @param endIndex the index to finish the search at.
|
||||
* @param from the index to start the search from.
|
||||
* @param to the index to finish the search at.
|
||||
* @return the index of the first occurrence of the byte in the buffer
|
||||
* within the given bounds, or <code>-1</code> if the byte does
|
||||
* within the given bounds, or {@code -1} if the byte does
|
||||
* not occur.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public int indexOf(byte b, int beginIndex, int endIndex) {
|
||||
public int indexOf(final byte b, final int from, final int to) {
|
||||
int beginIndex = from;
|
||||
if (beginIndex < 0) {
|
||||
beginIndex = 0;
|
||||
}
|
||||
int endIndex = to;
|
||||
if (endIndex > this.len) {
|
||||
endIndex = this.len;
|
||||
}
|
||||
@ -329,17 +327,17 @@ public final class ByteArrayBuffer implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the index within this buffer of the first occurrence of the
|
||||
* specified byte, starting the search at <code>0</code> and finishing
|
||||
* specified byte, starting the search at {@code 0} and finishing
|
||||
* at {@link #length()}. If no such byte occurs in this buffer within
|
||||
* those bounds, <code>-1</code> is returned.
|
||||
* those bounds, {@code -1} is returned.
|
||||
*
|
||||
* @param b the byte to search for.
|
||||
* @return the index of the first occurrence of the byte in the
|
||||
* buffer, or <code>-1</code> if the byte does not occur.
|
||||
* buffer, or {@code -1} if the byte does not occur.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public int indexOf(byte b) {
|
||||
public int indexOf(final byte b) {
|
||||
return indexOf(b, 0, this.len);
|
||||
}
|
||||
}
|
||||
@ -25,16 +25,20 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.foxinmy.weixin4j.http.apache;
|
||||
package com.foxinmy.weixin4j.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.nio.CharBuffer;
|
||||
|
||||
import com.foxinmy.weixin4j.http.HTTP;
|
||||
|
||||
|
||||
/**
|
||||
* A resizable char array.
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public final class CharArrayBuffer implements Serializable {
|
||||
public final class CharArrayBuffer implements CharSequence, Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6208952725094867135L;
|
||||
|
||||
@ -47,33 +51,30 @@ public final class CharArrayBuffer implements Serializable {
|
||||
*
|
||||
* @param capacity the capacity
|
||||
*/
|
||||
public CharArrayBuffer(int capacity) {
|
||||
public CharArrayBuffer(final int capacity) {
|
||||
super();
|
||||
if (capacity < 0) {
|
||||
throw new IllegalArgumentException("Buffer capacity may not be negative");
|
||||
}
|
||||
this.buffer = new char[capacity];
|
||||
}
|
||||
|
||||
private void expand(int newlen) {
|
||||
char newbuffer[] = new char[Math.max(this.buffer.length << 1, newlen)];
|
||||
private void expand(final int newlen) {
|
||||
final char newbuffer[] = new char[Math.max(this.buffer.length << 1, newlen)];
|
||||
System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
|
||||
this.buffer = newbuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends <code>len</code> chars to this buffer from the given source
|
||||
* array starting at index <code>off</code>. The capacity of the buffer
|
||||
* is increased, if necessary, to accommodate all <code>len</code> chars.
|
||||
* Appends {@code len} chars to this buffer from the given source
|
||||
* array starting at index {@code off}. The capacity of the buffer
|
||||
* is increased, if necessary, to accommodate all {@code len} chars.
|
||||
*
|
||||
* @param b the chars to be appended.
|
||||
* @param off the index of the first char to append.
|
||||
* @param len the number of chars to append.
|
||||
* @throws IndexOutOfBoundsException if <code>off</code> is out of
|
||||
* range, <code>len</code> is negative, or
|
||||
* <code>off</code> + <code>len</code> is out of range.
|
||||
* @throws IndexOutOfBoundsException if {@code off} is out of
|
||||
* range, {@code len} is negative, or
|
||||
* {@code off} + {@code len} is out of range.
|
||||
*/
|
||||
public void append(final char[] b, int off, int len) {
|
||||
public void append(final char[] b, final int off, final int len) {
|
||||
if (b == null) {
|
||||
return;
|
||||
}
|
||||
@ -84,7 +85,7 @@ public final class CharArrayBuffer implements Serializable {
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
int newlen = this.len + len;
|
||||
final int newlen = this.len + len;
|
||||
if (newlen > this.buffer.length) {
|
||||
expand(newlen);
|
||||
}
|
||||
@ -98,33 +99,31 @@ public final class CharArrayBuffer implements Serializable {
|
||||
*
|
||||
* @param str the string.
|
||||
*/
|
||||
public void append(String str) {
|
||||
if (str == null) {
|
||||
str = "null";
|
||||
}
|
||||
int strlen = str.length();
|
||||
int newlen = this.len + strlen;
|
||||
public void append(final String str) {
|
||||
final String s = str != null ? str : "null";
|
||||
final int strlen = s.length();
|
||||
final int newlen = this.len + strlen;
|
||||
if (newlen > this.buffer.length) {
|
||||
expand(newlen);
|
||||
}
|
||||
str.getChars(0, strlen, this.buffer, this.len);
|
||||
s.getChars(0, strlen, this.buffer, this.len);
|
||||
this.len = newlen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends <code>len</code> chars to this buffer from the given source
|
||||
* buffer starting at index <code>off</code>. The capacity of the
|
||||
* Appends {@code len} chars to this buffer from the given source
|
||||
* buffer starting at index {@code off}. The capacity of the
|
||||
* destination buffer is increased, if necessary, to accommodate all
|
||||
* <code>len</code> chars.
|
||||
* {@code len} chars.
|
||||
*
|
||||
* @param b the source buffer to be appended.
|
||||
* @param off the index of the first char to append.
|
||||
* @param len the number of chars to append.
|
||||
* @throws IndexOutOfBoundsException if <code>off</code> is out of
|
||||
* range, <code>len</code> is negative, or
|
||||
* <code>off</code> + <code>len</code> is out of range.
|
||||
* @throws IndexOutOfBoundsException if {@code off} is out of
|
||||
* range, {@code len} is negative, or
|
||||
* {@code off} + {@code len} is out of range.
|
||||
*/
|
||||
public void append(final CharArrayBuffer b, int off, int len) {
|
||||
public void append(final CharArrayBuffer b, final int off, final int len) {
|
||||
if (b == null) {
|
||||
return;
|
||||
}
|
||||
@ -133,7 +132,7 @@ public final class CharArrayBuffer implements Serializable {
|
||||
|
||||
/**
|
||||
* Appends all chars to this buffer from the given source buffer starting
|
||||
* at index <code>0</code>. The capacity of the destination buffer is
|
||||
* at index {@code 0}. The capacity of the destination buffer is
|
||||
* increased, if necessary, to accommodate all {@link #length()} chars.
|
||||
*
|
||||
* @param b the source buffer to be appended.
|
||||
@ -146,13 +145,13 @@ public final class CharArrayBuffer implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends <code>ch</code> char to this buffer. The capacity of the buffer
|
||||
* Appends {@code ch} char to this buffer. The capacity of the buffer
|
||||
* is increased, if necessary, to accommodate the additional char.
|
||||
*
|
||||
* @param ch the char to be appended.
|
||||
*/
|
||||
public void append(char ch) {
|
||||
int newlen = this.len + 1;
|
||||
public void append(final char ch) {
|
||||
final int newlen = this.len + 1;
|
||||
if (newlen > this.buffer.length) {
|
||||
expand(newlen);
|
||||
}
|
||||
@ -161,20 +160,20 @@ public final class CharArrayBuffer implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends <code>len</code> bytes to this buffer from the given source
|
||||
* array starting at index <code>off</code>. The capacity of the buffer
|
||||
* is increased, if necessary, to accommodate all <code>len</code> bytes.
|
||||
* Appends {@code len} bytes to this buffer from the given source
|
||||
* array starting at index {@code off}. The capacity of the buffer
|
||||
* is increased, if necessary, to accommodate all {@code len} bytes.
|
||||
* <p>
|
||||
* The bytes are converted to chars using simple cast.
|
||||
*
|
||||
* @param b the bytes to be appended.
|
||||
* @param off the index of the first byte to append.
|
||||
* @param len the number of bytes to append.
|
||||
* @throws IndexOutOfBoundsException if <code>off</code> is out of
|
||||
* range, <code>len</code> is negative, or
|
||||
* <code>off</code> + <code>len</code> is out of range.
|
||||
* @throws IndexOutOfBoundsException if {@code off} is out of
|
||||
* range, {@code len} is negative, or
|
||||
* {@code off} + {@code len} is out of range.
|
||||
*/
|
||||
public void append(final byte[] b, int off, int len) {
|
||||
public void append(final byte[] b, final int off, final int len) {
|
||||
if (b == null) {
|
||||
return;
|
||||
}
|
||||
@ -185,8 +184,8 @@ public final class CharArrayBuffer implements Serializable {
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
int oldlen = this.len;
|
||||
int newlen = oldlen + len;
|
||||
final int oldlen = this.len;
|
||||
final int newlen = oldlen + len;
|
||||
if (newlen > this.buffer.length) {
|
||||
expand(newlen);
|
||||
}
|
||||
@ -197,20 +196,20 @@ public final class CharArrayBuffer implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends <code>len</code> bytes to this buffer from the given source
|
||||
* array starting at index <code>off</code>. The capacity of the buffer
|
||||
* is increased, if necessary, to accommodate all <code>len</code> bytes.
|
||||
* Appends {@code len} bytes to this buffer from the given source
|
||||
* array starting at index {@code off}. The capacity of the buffer
|
||||
* is increased, if necessary, to accommodate all {@code len} bytes.
|
||||
* <p>
|
||||
* The bytes are converted to chars using simple cast.
|
||||
*
|
||||
* @param b the bytes to be appended.
|
||||
* @param off the index of the first byte to append.
|
||||
* @param len the number of bytes to append.
|
||||
* @throws IndexOutOfBoundsException if <code>off</code> is out of
|
||||
* range, <code>len</code> is negative, or
|
||||
* <code>off</code> + <code>len</code> is out of range.
|
||||
* @throws IndexOutOfBoundsException if {@code off} is out of
|
||||
* range, {@code len} is negative, or
|
||||
* {@code off} + {@code len} is out of range.
|
||||
*/
|
||||
public void append(final ByteArrayBuffer b, int off, int len) {
|
||||
public void append(final ByteArrayBuffer b, final int off, final int len) {
|
||||
if (b == null) {
|
||||
return;
|
||||
}
|
||||
@ -241,7 +240,7 @@ public final class CharArrayBuffer implements Serializable {
|
||||
* @return char array
|
||||
*/
|
||||
public char[] toCharArray() {
|
||||
char[] b = new char[this.len];
|
||||
final char[] b = new char[this.len];
|
||||
if (this.len > 0) {
|
||||
System.arraycopy(this.buffer, 0, b, 0, this.len);
|
||||
}
|
||||
@ -249,16 +248,17 @@ public final class CharArrayBuffer implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>char</code> value in this buffer at the specified
|
||||
* Returns the {@code char} value in this buffer at the specified
|
||||
* index. The index argument must be greater than or equal to
|
||||
* <code>0</code>, and less than the length of this buffer.
|
||||
* {@code 0}, and less than the length of this buffer.
|
||||
*
|
||||
* @param i the index of the desired char value.
|
||||
* @return the char value at the specified index.
|
||||
* @throws IndexOutOfBoundsException if <code>index</code> is
|
||||
* @throws IndexOutOfBoundsException if {@code index} is
|
||||
* negative or greater than or equal to {@link #length()}.
|
||||
*/
|
||||
public char charAt(int i) {
|
||||
@Override
|
||||
public char charAt(final int i) {
|
||||
return this.buffer[i];
|
||||
}
|
||||
|
||||
@ -287,6 +287,7 @@ public final class CharArrayBuffer implements Serializable {
|
||||
*
|
||||
* @return the length of the buffer
|
||||
*/
|
||||
@Override
|
||||
public int length() {
|
||||
return this.len;
|
||||
}
|
||||
@ -294,16 +295,16 @@ public final class CharArrayBuffer implements Serializable {
|
||||
/**
|
||||
* Ensures that the capacity is at least equal to the specified minimum.
|
||||
* If the current capacity is less than the argument, then a new internal
|
||||
* array is allocated with greater capacity. If the <code>required</code>
|
||||
* array is allocated with greater capacity. If the {@code required}
|
||||
* argument is non-positive, this method takes no action.
|
||||
*
|
||||
* @param required the minimum required capacity.
|
||||
*/
|
||||
public void ensureCapacity(int required) {
|
||||
public void ensureCapacity(final int required) {
|
||||
if (required <= 0) {
|
||||
return;
|
||||
}
|
||||
int available = this.buffer.length - this.len;
|
||||
final int available = this.buffer.length - this.len;
|
||||
if (required > available) {
|
||||
expand(this.len + required);
|
||||
}
|
||||
@ -312,14 +313,14 @@ public final class CharArrayBuffer implements Serializable {
|
||||
/**
|
||||
* Sets the length of the buffer. The new length value is expected to be
|
||||
* less than the current capacity and greater than or equal to
|
||||
* <code>0</code>.
|
||||
* {@code 0}.
|
||||
*
|
||||
* @param len the new length
|
||||
* @throws IndexOutOfBoundsException if the
|
||||
* <code>len</code> argument is greater than the current
|
||||
* capacity of the buffer or less than <code>0</code>.
|
||||
* {@code len} argument is greater than the current
|
||||
* capacity of the buffer or less than {@code 0}.
|
||||
*/
|
||||
public void setLength(int len) {
|
||||
public void setLength(final int len) {
|
||||
if (len < 0 || len > this.buffer.length) {
|
||||
throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.buffer.length);
|
||||
}
|
||||
@ -327,9 +328,9 @@ public final class CharArrayBuffer implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if this buffer is empty, that is, its
|
||||
* {@link #length()} is equal to <code>0</code>.
|
||||
* @return <code>true</code> if this buffer is empty, <code>false</code>
|
||||
* Returns {@code true} if this buffer is empty, that is, its
|
||||
* {@link #length()} is equal to {@code 0}.
|
||||
* @return {@code true} if this buffer is empty, {@code false}
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
@ -337,9 +338,9 @@ public final class CharArrayBuffer implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if this buffer is full, that is, its
|
||||
* Returns {@code true} if this buffer is full, that is, its
|
||||
* {@link #length()} is equal to its {@link #capacity()}.
|
||||
* @return <code>true</code> if this buffer is full, <code>false</code>
|
||||
* @return {@code true} if this buffer is full, {@code false}
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isFull() {
|
||||
@ -349,28 +350,30 @@ public final class CharArrayBuffer implements Serializable {
|
||||
/**
|
||||
* Returns the index within this buffer of the first occurrence of the
|
||||
* specified character, starting the search at the specified
|
||||
* <code>beginIndex</code> and finishing at <code>endIndex</code>.
|
||||
* {@code beginIndex} and finishing at {@code endIndex}.
|
||||
* If no such character occurs in this buffer within the specified bounds,
|
||||
* <code>-1</code> is returned.
|
||||
* {@code -1} is returned.
|
||||
* <p>
|
||||
* There is no restriction on the value of <code>beginIndex</code> and
|
||||
* <code>endIndex</code>. If <code>beginIndex</code> is negative,
|
||||
* it has the same effect as if it were zero. If <code>endIndex</code> is
|
||||
* There is no restriction on the value of {@code beginIndex} and
|
||||
* {@code endIndex}. If {@code beginIndex} is negative,
|
||||
* it has the same effect as if it were zero. If {@code endIndex} is
|
||||
* greater than {@link #length()}, it has the same effect as if it were
|
||||
* {@link #length()}. If the <code>beginIndex</code> is greater than
|
||||
* the <code>endIndex</code>, <code>-1</code> is returned.
|
||||
* {@link #length()}. If the {@code beginIndex} is greater than
|
||||
* the {@code endIndex}, {@code -1} is returned.
|
||||
*
|
||||
* @param ch the char to search for.
|
||||
* @param beginIndex the index to start the search from.
|
||||
* @param endIndex the index to finish the search at.
|
||||
* @param from the index to start the search from.
|
||||
* @param to the index to finish the search at.
|
||||
* @return the index of the first occurrence of the character in the buffer
|
||||
* within the given bounds, or <code>-1</code> if the character does
|
||||
* within the given bounds, or {@code -1} if the character does
|
||||
* not occur.
|
||||
*/
|
||||
public int indexOf(int ch, int beginIndex, int endIndex) {
|
||||
public int indexOf(final int ch, final int from, final int to) {
|
||||
int beginIndex = from;
|
||||
if (beginIndex < 0) {
|
||||
beginIndex = 0;
|
||||
}
|
||||
int endIndex = to;
|
||||
if (endIndex > this.len) {
|
||||
endIndex = this.len;
|
||||
}
|
||||
@ -387,53 +390,33 @@ public final class CharArrayBuffer implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the index within this buffer of the first occurrence of the
|
||||
* specified character, starting the search at <code>0</code> and finishing
|
||||
* specified character, starting the search at {@code 0} and finishing
|
||||
* at {@link #length()}. If no such character occurs in this buffer within
|
||||
* those bounds, <code>-1</code> is returned.
|
||||
* those bounds, {@code -1} is returned.
|
||||
*
|
||||
* @param ch the char to search for.
|
||||
* @return the index of the first occurrence of the character in the
|
||||
* buffer, or <code>-1</code> if the character does not occur.
|
||||
* buffer, or {@code -1} if the character does not occur.
|
||||
*/
|
||||
public int indexOf(int ch) {
|
||||
public int indexOf(final int ch) {
|
||||
return indexOf(ch, 0, this.len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a substring of this buffer. The substring begins at the specified
|
||||
* <code>beginIndex</code> and extends to the character at index
|
||||
* <code>endIndex - 1</code>.
|
||||
* {@code beginIndex} and extends to the character at index
|
||||
* {@code endIndex - 1}.
|
||||
*
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @param endIndex the ending index, exclusive.
|
||||
* @return the specified substring.
|
||||
* @exception StringIndexOutOfBoundsException if the
|
||||
* <code>beginIndex</code> is negative, or
|
||||
* <code>endIndex</code> is larger than the length of this
|
||||
* buffer, or <code>beginIndex</code> is larger than
|
||||
* <code>endIndex</code>.
|
||||
* @throws StringIndexOutOfBoundsException if the
|
||||
* {@code beginIndex} is negative, or
|
||||
* {@code endIndex} is larger than the length of this
|
||||
* buffer, or {@code beginIndex} is larger than
|
||||
* {@code endIndex}.
|
||||
*/
|
||||
public String substring(int beginIndex, int endIndex) {
|
||||
return new String(this.buffer, beginIndex, endIndex - beginIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a substring of this buffer with leading and trailing whitespace
|
||||
* omitted. The substring begins with the first non-whitespace character
|
||||
* from <code>beginIndex</code> and extends to the last
|
||||
* non-whitespace character with the index lesser than
|
||||
* <code>endIndex</code>.
|
||||
*
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @param endIndex the ending index, exclusive.
|
||||
* @return the specified substring.
|
||||
* @exception IndexOutOfBoundsException if the
|
||||
* <code>beginIndex</code> is negative, or
|
||||
* <code>endIndex</code> is larger than the length of this
|
||||
* buffer, or <code>beginIndex</code> is larger than
|
||||
* <code>endIndex</code>.
|
||||
*/
|
||||
public String substringTrimmed(int beginIndex, int endIndex) {
|
||||
public String substring(final int beginIndex, final int endIndex) {
|
||||
if (beginIndex < 0) {
|
||||
throw new IndexOutOfBoundsException("Negative beginIndex: " + beginIndex);
|
||||
}
|
||||
@ -443,15 +426,64 @@ public final class CharArrayBuffer implements Serializable {
|
||||
if (beginIndex > endIndex) {
|
||||
throw new IndexOutOfBoundsException("beginIndex: " + beginIndex + " > endIndex: " + endIndex);
|
||||
}
|
||||
while (beginIndex < endIndex && HTTP.isWhitespace(this.buffer[beginIndex])) {
|
||||
beginIndex++;
|
||||
}
|
||||
while (endIndex > beginIndex && HTTP.isWhitespace(this.buffer[endIndex - 1])) {
|
||||
endIndex--;
|
||||
}
|
||||
return new String(this.buffer, beginIndex, endIndex - beginIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a substring of this buffer with leading and trailing whitespace
|
||||
* omitted. The substring begins with the first non-whitespace character
|
||||
* from {@code beginIndex} and extends to the last
|
||||
* non-whitespace character with the index lesser than
|
||||
* {@code endIndex}.
|
||||
*
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @param endIndex the ending index, exclusive.
|
||||
* @return the specified substring.
|
||||
* @throws IndexOutOfBoundsException if the
|
||||
* {@code beginIndex} is negative, or
|
||||
* {@code endIndex} is larger than the length of this
|
||||
* buffer, or {@code beginIndex} is larger than
|
||||
* {@code endIndex}.
|
||||
*/
|
||||
public String substringTrimmed(final int beginIndex, final int endIndex) {
|
||||
if (beginIndex < 0) {
|
||||
throw new IndexOutOfBoundsException("Negative beginIndex: " + beginIndex);
|
||||
}
|
||||
if (endIndex > this.len) {
|
||||
throw new IndexOutOfBoundsException("endIndex: " + endIndex + " > length: " + this.len);
|
||||
}
|
||||
if (beginIndex > endIndex) {
|
||||
throw new IndexOutOfBoundsException("beginIndex: " + beginIndex + " > endIndex: " + endIndex);
|
||||
}
|
||||
int beginIndex0 = beginIndex;
|
||||
int endIndex0 = endIndex;
|
||||
while (beginIndex0 < endIndex && HTTP.isWhitespace(this.buffer[beginIndex0])) {
|
||||
beginIndex0++;
|
||||
}
|
||||
while (endIndex0 > beginIndex0 && HTTP.isWhitespace(this.buffer[endIndex0 - 1])) {
|
||||
endIndex0--;
|
||||
}
|
||||
return new String(this.buffer, beginIndex0, endIndex0 - beginIndex0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @since 4.4
|
||||
*/
|
||||
@Override
|
||||
public CharSequence subSequence(final int beginIndex, final int endIndex) {
|
||||
if (beginIndex < 0) {
|
||||
throw new IndexOutOfBoundsException("Negative beginIndex: " + beginIndex);
|
||||
}
|
||||
if (endIndex > this.len) {
|
||||
throw new IndexOutOfBoundsException("endIndex: " + endIndex + " > length: " + this.len);
|
||||
}
|
||||
if (beginIndex > endIndex) {
|
||||
throw new IndexOutOfBoundsException("beginIndex: " + beginIndex + " > endIndex: " + endIndex);
|
||||
}
|
||||
return CharBuffer.wrap(this.buffer, beginIndex, endIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new String(this.buffer, 0, this.len);
|
||||
@ -10,8 +10,8 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.http.MimeType;
|
||||
import com.foxinmy.weixin4j.http.apache.FormBodyPart;
|
||||
import com.foxinmy.weixin4j.http.apache.InputStreamBody;
|
||||
import com.foxinmy.weixin4j.http.apache.content.InputStreamBody;
|
||||
import com.foxinmy.weixin4j.http.apache.mime.FormBodyPart;
|
||||
import com.foxinmy.weixin4j.http.weixin.ApiResult;
|
||||
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
||||
import com.foxinmy.weixin4j.model.Token;
|
||||
|
||||
@ -19,10 +19,10 @@ import com.foxinmy.weixin4j.http.HttpMethod;
|
||||
import com.foxinmy.weixin4j.http.HttpRequest;
|
||||
import com.foxinmy.weixin4j.http.HttpResponse;
|
||||
import com.foxinmy.weixin4j.http.MimeType;
|
||||
import com.foxinmy.weixin4j.http.apache.ByteArrayBody;
|
||||
import com.foxinmy.weixin4j.http.apache.FormBodyPart;
|
||||
import com.foxinmy.weixin4j.http.apache.InputStreamBody;
|
||||
import com.foxinmy.weixin4j.http.apache.StringBody;
|
||||
import com.foxinmy.weixin4j.http.apache.content.ByteArrayBody;
|
||||
import com.foxinmy.weixin4j.http.apache.content.InputStreamBody;
|
||||
import com.foxinmy.weixin4j.http.apache.content.StringBody;
|
||||
import com.foxinmy.weixin4j.http.apache.mime.FormBodyPart;
|
||||
import com.foxinmy.weixin4j.http.entity.StringEntity;
|
||||
import com.foxinmy.weixin4j.http.weixin.ApiResult;
|
||||
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
||||
|
||||
@ -22,9 +22,9 @@ import com.foxinmy.weixin4j.http.HttpMethod;
|
||||
import com.foxinmy.weixin4j.http.HttpRequest;
|
||||
import com.foxinmy.weixin4j.http.HttpResponse;
|
||||
import com.foxinmy.weixin4j.http.MimeType;
|
||||
import com.foxinmy.weixin4j.http.apache.ByteArrayBody;
|
||||
import com.foxinmy.weixin4j.http.apache.FormBodyPart;
|
||||
import com.foxinmy.weixin4j.http.apache.InputStreamBody;
|
||||
import com.foxinmy.weixin4j.http.apache.content.ByteArrayBody;
|
||||
import com.foxinmy.weixin4j.http.apache.content.InputStreamBody;
|
||||
import com.foxinmy.weixin4j.http.apache.mime.FormBodyPart;
|
||||
import com.foxinmy.weixin4j.http.weixin.ApiResult;
|
||||
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
||||
import com.foxinmy.weixin4j.model.Token;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user