
commons.http.WwwFormUrlEncodedCodec Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2017 Georgi Pavlov ([email protected]).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the MIT license which accompanies
* this distribution, and is available at
* https://github.com/tengia/oauth-2/blob/master/LICENSE
*/
package commons.http;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Encoder/decoder for the x-www-form-urlencoded data format.
*
*/
public class WwwFormUrlEncodedCodec {
@SuppressWarnings("rawtypes")
private Map globalSerializerMappings = new HashMap<>();
@SuppressWarnings("rawtypes")
private Map globalDeseralizerMappings = new HashMap<>();
public static interface Serializer {
String serialize(T value);
}
public static interface Deserializer {
T deserialize(String input);
}
public WwwFormUrlEncodedCodec with(String mappedName, Serializer serializer) {
this.globalSerializerMappings.put(mappedName, serializer);
return this;
}
@SuppressWarnings("rawtypes")
protected Map resolveSerializerMappings(Map serializersMappings) {
//resolve mappings to use. We start with globals if any, then override them with specifically provided as argument in this method if any
Map mappings = this.globalSerializerMappings;
if(serializersMappings != null){
mappings = Stream.of(serializersMappings)
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(Collectors.toMap(Entry::getKey, Entry::getValue,
(v1, v2) -> v2,
() -> new HashMap<>(this.globalSerializerMappings)));
}
return mappings;
}
@SuppressWarnings("unchecked")
protected Entry encodeEntry(final Entry entry, @SuppressWarnings("rawtypes") final Map mappings) {
String encodedValue = null;
//encode values with mapped serializers if any
encodedValue = mappings.entrySet().stream()
.filter(serializerMapping -> entry.getKey().equals(serializerMapping.getKey()))
.map(Map.Entry::getValue)
.findFirst()
.flatMap((serializer)->{
return Optional.ofNullable((String)serializer.serialize(entry.getValue()));
})
.orElseGet(()-> null);
if(encodedValue == null) {
//no special serializers were used. We will use toString() if the value is not a string already
if(!String.class.isAssignableFrom(entry.getValue().getClass()))
encodedValue = entry.getValue().toString();
else
encodedValue = (String) entry.getValue();//TODO: refactor to avoid unnecessary change of the entry
}
entry.setValue(encodedValue);
return entry;
}
/**
* Transform a stream of key-value pairs into a www.form-encoded string.
* @param stream
* @return
*/
public String encodeStream(final Stream> stream, @SuppressWarnings("rawtypes") Map serializersMappings) {
@SuppressWarnings("rawtypes")
final Map mappings = resolveSerializerMappings(serializersMappings);
String encoded = stream.filter(parameterEntry -> parameterEntry.getValue() != null)
.map(parameterEntry -> {
return encodeEntry(parameterEntry, mappings);
})
.map((entry) -> {
return entry.getKey() + "=" + entry.getValue();
})
.collect(Collectors.joining("&"));
return encoded;
}
public WwwFormUrlEncodedCodec() {
super();
}
public WwwFormUrlEncodedCodec with(String mappedName, Deserializer deserializer) {
globalDeseralizerMappings.put(mappedName, deserializer);
return this;
}
public final class Tuple implements Map.Entry {
String k, v;
Tuple(String key, String value){
this.k = key;
this.v = value;
}
@Override
public String getKey() {
return this.k;
}
@Override
public String getValue() {
return this.v;
}
@Override
public String setValue(String value) {
String old = this.v;
this.v = value;
return old;
}
}
public Stream decodeStream(final String encodedString) {
if(encodedString == null)
throw new IllegalArgumentException();
String decodedString = null;
try {
decodedString = URLDecoder.decode(encodedString, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) { throw new RuntimeException(e);}
return Stream.of(decodedString.split("&"))
.map((entry)->{
String[] kv = entry.split("=");
return new Tuple(kv[0], kv[1]);
});
}
@SuppressWarnings("rawtypes")
protected Map resolveDeserializerMappings(Map deserializersMappings) {
//resolve mappings to use. We start with globals if any, then override them with specifically provided as argument in this method if any
Map mappings = this.globalDeseralizerMappings;
if(deserializersMappings != null){
mappings = Stream.of(deserializersMappings)
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(Collectors.toMap(Entry::getKey, Entry::getValue,
(v1, v2) -> v2,
() -> new HashMap<>(this.globalDeseralizerMappings)));
}
return mappings;
}
protected Object decodeEntry(Tuple tuple, @SuppressWarnings("rawtypes") final Map deserializersMappings) {
Object result = deserializersMappings.entrySet().stream()
.filter(entry -> tuple.getKey().equals(entry.getKey()))
.map(Map.Entry::getValue)
.findFirst()
.flatMap((deser)->{
return Optional.ofNullable(deser.deserialize(tuple.getValue()));
})
.orElseGet(()->null);
if(result == null)
result = tuple.getValue();
return result;
}
public Map from(final String encodedString, @SuppressWarnings("rawtypes") final Map deserializersMappings) {
@SuppressWarnings("rawtypes")
final Map mappings = resolveDeserializerMappings(deserializersMappings);
Map parameters = this.decodeStream(encodedString)
.collect(Collectors.toMap(Tuple::getKey, (tuple)-> {
return decodeEntry(tuple, mappings);
}));
return parameters;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy