forked from googleapis/google-http-java-client
/
UrlEncodedContent.java
177 lines (163 loc) · 5.58 KB
/
UrlEncodedContent.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*
* Copyright (c) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.api.client.http;
import com.google.api.client.util.Data;
import com.google.api.client.util.FieldInfo;
import com.google.api.client.util.Preconditions;
import com.google.api.client.util.Types;
import com.google.api.client.util.escape.CharEscapers;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
/**
* Implements support for HTTP form content encoding serialization of type {@code
* application/x-www-form-urlencoded} as specified in the <a href=
* "http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4.1">HTML 4.0 Specification</a>.
*
* <p>Sample usage:
*
* <pre>
* static void setContent(HttpRequest request, Object item) {
* request.setContent(new UrlEncodedContent(item));
* }
* </pre>
*
* <p>Implementation is not thread-safe.
*
* @author Yaniv Inbar
* @since 1.0
*/
public class UrlEncodedContent extends AbstractHttpContent {
/** Key name/value data. */
private Object data;
/** Use URI Path encoder flag. False by default (use legacy and deprecated escapeUri) */
private boolean uriPathEncodingFlag;
/**
* Initialize the UrlEncodedContent with the legacy and deprecated escapeUri encoder
* @param data key name/value data
* */
public UrlEncodedContent(Object data) {
super(UrlEncodedParser.MEDIA_TYPE);
setData(data);
this.uriPathEncodingFlag = false;
}
/**
* Initialize the UrlEncodedContent with our without the legacy and deprecated escapeUri encoder
* @param data key name/value data
* @param useUriPathEncoding Escapes the string value so it can be safely included in URI path segments.
* For details on escaping URIs, see <a href="http://tools.ietf.org/html/rfc3986#section-2.4">RFC 3986 -
* section 2.4</a>
*/
public UrlEncodedContent(Object data, Boolean useUriPathEncoding) {
super(UrlEncodedParser.MEDIA_TYPE);
setData(data);
this.uriPathEncodingFlag = useUriPathEncoding;
}
@Override
public void writeTo(OutputStream out) throws IOException {
Writer writer = new BufferedWriter(new OutputStreamWriter(out, getCharset()));
boolean first = true;
for (Map.Entry<String, Object> nameValueEntry : Data.mapOf(data).entrySet()) {
Object value = nameValueEntry.getValue();
if (value != null) {
String name = CharEscapers.escapeUri(nameValueEntry.getKey());
Class<? extends Object> valueClass = value.getClass();
if (value instanceof Iterable<?> || valueClass.isArray()) {
for (Object repeatedValue : Types.iterableOf(value)) {
first = appendParam(first, writer, name, repeatedValue, this.uriPathEncodingFlag);
}
} else {
first = appendParam(first, writer, name, value, this.uriPathEncodingFlag);
}
}
}
writer.flush();
}
@Override
public UrlEncodedContent setMediaType(HttpMediaType mediaType) {
super.setMediaType(mediaType);
return this;
}
/**
* Returns the key name/value data or {@code null} for none.
*
* @since 1.5
*/
public final Object getData() {
return data;
}
/**
* Sets the key name/value data.
*
* <p>Overriding is only supported for the purpose of calling the super implementation and
* changing the return type, but nothing else.
*
* @since 1.5
*/
public UrlEncodedContent setData(Object data) {
this.data = Preconditions.checkNotNull(data);
return this;
}
/**
* Returns the URL-encoded content of the given HTTP request, or if none return and set as content
* a new instance of {@link UrlEncodedContent} (whose {@link #getData()} is an implementation of
* {@link Map}).
*
* @param request HTTP request
* @return URL-encoded content
* @throws ClassCastException if the HTTP request has a content defined that is not {@link
* UrlEncodedContent}
* @since 1.7
*/
public static UrlEncodedContent getContent(HttpRequest request) {
HttpContent content = request.getContent();
if (content != null) {
return (UrlEncodedContent) content;
}
UrlEncodedContent result = new UrlEncodedContent(new HashMap<String, Object>());
request.setContent(result);
return result;
}
private static boolean appendParam(
boolean first, Writer writer, String name, Object value, boolean uriPathEncodingFlag)
throws IOException {
// ignore nulls
if (value == null || Data.isNull(value)) {
return first;
}
// append value
if (first) {
first = false;
} else {
writer.write("&");
}
writer.write(name);
String stringValue =
value instanceof Enum<?> ? FieldInfo.of((Enum<?>) value).getName() : value.toString();
if (uriPathEncodingFlag) {
stringValue = CharEscapers.escapeUriPath(stringValue);
} else {
stringValue = CharEscapers.escapeUri(stringValue);
}
if (stringValue.length() != 0) {
writer.write("=");
writer.write(stringValue);
}
return first;
}
}