http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer.java new file mode 100755 index 0000000..e4894df --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer.java @@ -0,0 +1,515 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2011, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.urlencoding; + +import static com.ibm.juno.core.urlencoding.UonSerializerProperties.*; +import static com.ibm.juno.core.urlencoding.UrlEncodingProperties.*; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.util.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.annotation.*; +import com.ibm.juno.core.filter.*; +import com.ibm.juno.core.serializer.*; +import com.ibm.juno.core.utils.*; + +/** + * Serializes POJO models to URL-encoded notation with UON-encoded values (a notation for URL-encoded query paramter values). + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Accept</code> types: <code>application/x-www-form-urlencoded</code> + * <p> + * Produces <code>Content-Type</code> types: <code>application/x-www-form-urlencoded</code> + * + * + * <h6 class='topic'>Description</h6> + * <p> + * This serializer provides several serialization options. Typically, one of the predefined DEFAULT serializers will be sufficient. + * However, custom serializers can be constructed to fine-tune behavior. + * + * + * <h6 class='topic'>Configurable properties</h6> + * <p> + * This class has the following properties associated with it: + * <ul> + * <li>{@link UonSerializerProperties} + * <li>{@link SerializerProperties} + * <li>{@link BeanContextProperties} + * </ul> + * <p> + * The following shows a sample object defined in Javascript: + * </p> + * <p class='bcode'> + * { + * id: 1, + * name: <js>'John Smith'</js>, + * uri: <js>'http://sample/addressBook/person/1'</js>, + * addressBookUri: <js>'http://sample/addressBook'</js>, + * birthDate: <js>'1946-08-12T00:00:00Z'</js>, + * otherIds: <jk>null</jk>, + * addresses: [ + * { + * uri: <js>'http://sample/addressBook/address/1'</js>, + * personUri: <js>'http://sample/addressBook/person/1'</js>, + * id: 1, + * street: <js>'100 Main Street'</js>, + * city: <js>'Anywhereville'</js>, + * state: <js>'NY'</js>, + * zip: 12345, + * isCurrent: <jk>true</jk>, + * } + * ] + * } + * </p> + * <p> + * Using the "strict" syntax defined in this document, the equivalent + * URL-encoded notation would be as follows: + * </p> + * <p class='bcode'> + * <xa>id</xa>=$n(<xs>1</xs>) + * &<xa>name</xa>=<xs>John+Smith</xs>, + * &<xa>uri</xa>=<xs>http://sample/addressBook/person/1</xs>, + * &<xa>addressBookUri</xa>=<xs>http://sample/addressBook</xs>, + * &<xa>birthDate</xa>=<xs>1946-08-12T00:00:00Z</xs>, + * &<xa>otherIds</xa>=<xs>%00</xs>, + * &<xa>addresses</xa>=$a( + * $o( + * <xa>uri</xa>=<xs>http://sample/addressBook/address/1</xs>, + * <xa>personUri</xa>=<xs>http://sample/addressBook/person/1</xs>, + * <xa>id</xa>=$n(<xs>1</xs>), + * <xa>street</xa>=<xs>100+Main+Street</xs>, + * <xa>city</xa>=<xs>Anywhereville</xs>, + * <xa>state</xa>=<xs>NY</xs>, + * <xa>zip</xa>=$n(<xs>12345</xs>), + * <xa>isCurrent</xa>=$b(<xs>true</xs>) + * ) + * ) + * </p> + * <p> + * A secondary "lax" syntax is available when the data type of the + * values are already known on the receiving end of the transmission: + * </p> + * <p class='bcode'> + * <xa>id</xa>=<xs>1</xs>, + * &<xa>name</xa>=<xs>John+Smith</xs>, + * &<xa>uri</xa>=<xs>http://sample/addressBook/person/1</xs>, + * &<xa>addressBookUri</xa>=<xs>http://sample/addressBook</xs>, + * &<xa>birthDate</xa>=<xs>1946-08-12T00:00:00Z</xs>, + * &<xa>otherIds</xa>=<xs>%00</xs>, + * &<xa>addresses</xa>=( + * ( + * <xa>uri</xa>=<xs>http://sample/addressBook/address/1</xs>, + * <xa>personUri</xa>=<xs>http://sample/addressBook/person/1</xs>, + * <xa>id</xa>=<xs>1</xs>, + * <xa>street</xa>=<xs>100+Main+Street</xs>, + * <xa>city</xa>=<xs>Anywhereville</xs>, + * <xa>state</xa>=<xs>NY</xs>, + * <xa>zip</xa>=<xs>12345</xs>, + * <xa>isCurrent</xa>=<xs>true</xs> + * ) + * ) + * </p> + * + * + * <h6 class='topic'>Examples</h6> + * <p class='bcode'> + * <jc>// Serialize a Map</jc> + * Map m = <jk>new</jk> ObjectMap(<js>"{a:'b',c:1,d:false,e:['f',1,false],g:{h:'i'}}"</js>); + * + * <jc>// Serialize to value equivalent to JSON.</jc> + * <jc>// Produces "a=b&c=$n(1)&d=$b(false)&e=$a(f,$n(1),$b(false))&g=$o(h=i)"</jc> + * String s = UrlEncodingSerializer.<jsf>DEFAULT</jsf>.serialize(s); + * + * <jc>// Serialize to simplified value (for when data type is already known by receiver).</jc> + * <jc>// Produces "a=b&c=1&d=false&e=(f,1,false)&g=(h=i))"</jc> + * String s = UrlEncodingSerializer.<jsf>DEFAULT_SIMPLE</jsf>.serialize(s); + * + * <jc>// Serialize a bean</jc> + * <jk>public class</jk> Person { + * <jk>public</jk> Person(String s); + * <jk>public</jk> String getName(); + * <jk>public int</jk> getAge(); + * <jk>public</jk> Address getAddress(); + * <jk>public boolean</jk> deceased; + * } + * + * <jk>public class</jk> Address { + * <jk>public</jk> String getStreet(); + * <jk>public</jk> String getCity(); + * <jk>public</jk> String getState(); + * <jk>public int</jk> getZip(); + * } + * + * Person p = <jk>new</jk> Person(<js>"John Doe"</js>, 23, <js>"123 Main St"</js>, <js>"Anywhere"</js>, <js>"NY"</js>, 12345, <jk>false</jk>); + * + * <jc>// Produces "name=John+Doe&age=23&address=$o(street=123+Main+St,city=Anywhere,state=NY,zip=$n(12345))&deceased=$b(false)"</jc> + * String s = UrlEncodingSerializer.<jsf>DEFAULT</jsf>.serialize(s); + * + * <jc>// Produces "name=John+Doe&age=23&address=(street=123+Main+St,city=Anywhere,state=NY,zip=12345)&deceased=false)"</jc> + * String s = UrlEncodingSerializer.<jsf>DEFAULT_SIMPLE</jsf>.serialize(s); + * </p> + * + * @author James Bognar ([email protected]) + */ +@Produces("application/x-www-form-urlencoded") +@SuppressWarnings("hiding") +public class UrlEncodingSerializer extends UonSerializer { + + /** Reusable instance of {@link UrlEncodingSerializer}, all default settings. */ + public static final UrlEncodingSerializer DEFAULT = new UrlEncodingSerializer().lock(); + + /** Reusable instance of {@link UrlEncodingSerializer.Simple}. */ + public static final UrlEncodingSerializer DEFAULT_SIMPLE = new Simple().lock(); + + /** Reusable instance of {@link UrlEncodingSerializer.SimpleExpanded}. */ + public static final UrlEncodingSerializer DEFAULT_SIMPLE_EXPANDED = new SimpleExpanded().lock(); + + /** Reusable instance of {@link UrlEncodingSerializer.Readable}. */ + public static final UrlEncodingSerializer DEFAULT_READABLE = new Readable().lock(); + + /** + * Constructor. + */ + public UrlEncodingSerializer() { + setProperty(UON_encodeChars, true); + } + + /** + * Equivalent to <code><jk>new</jk> UrlEncodingSerializer().setProperty(UonSerializerProperties.<jsf>UON_simpleMode</jsf>,<jk>true</jk>);</code>. + */ + @Produces(value={"application/x-www-form-urlencoded-simple"},contentType="application/x-www-form-urlencoded") + public static class Simple extends UrlEncodingSerializer { + /** Constructor */ + public Simple() { + setProperty(UON_simpleMode, true); + } + } + + /** + * Equivalent to <code><jk>new</jk> UrlEncodingSerializer().setProperty(UonSerializerProperties.<jsf>UON_simpleMode</jsf>,<jk>true</jk>).setProperty(UonSerializerProperties.<jsf>URLENC_expandedParams</jsf>,<jk>true</jk>);</code>. + */ + @Produces(value={"application/x-www-form-urlencoded-simple"},contentType="application/x-www-form-urlencoded") + public static class SimpleExpanded extends Simple { + /** Constructor */ + public SimpleExpanded() { + setProperty(URLENC_expandedParams, true); + } + } + + /** + * Equivalent to <code><jk>new</jk> UrlEncodingSerializer().setProperty(UonSerializerProperties.<jsf>UON_useWhitespace</jsf>,<jk>true</jk>);</code>. + */ + public static class Readable extends UrlEncodingSerializer { + /** Constructor */ + public Readable() { + setProperty(UON_useWhitespace, true); + } + } + + /** + * Workhorse method. Determines the type of object, and then calls the + * appropriate type-specific serialization method. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + private SerializerWriter serializeAnything(UonSerializerWriter out, Object o, UonSerializerContext ctx) throws SerializeException { + try { + + BeanContext bc = ctx.getBeanContext(); + + boolean addClassAttr; // Add "_class" attribute to element? + ClassMeta<?> aType; // The actual type + ClassMeta<?> gType; // The generic type + + aType = ctx.push("root", o, object()); + ctx.indent--; + if (aType == null) + aType = object(); + + gType = aType.getFilteredClassMeta(); + addClassAttr = (ctx.isAddClassAttrs()); + + // Filter if necessary + PojoFilter filter = aType.getPojoFilter(); // The filter + if (filter != null) { + o = filter.filter(o); + + // If the filter's getFilteredClass() method returns Object, we need to figure out + // the actual type now. + if (gType.isObject()) + gType = bc.getClassMetaForObject(o); + } + + if (gType.isMap()) { + if (o instanceof BeanMap) + serializeBeanMap(out, (BeanMap)o, addClassAttr, ctx); + else + serializeMap(out, (Map)o, gType, ctx); + } else if (gType.hasToObjectMapMethod()) { + serializeMap(out, gType.toObjectMap(o), gType, ctx); + } else if (gType.isBean()) { + serializeBeanMap(out, bc.forBean(o), addClassAttr, ctx); + } else if (gType.isCollection()) { + serializeMap(out, getCollectionMap((Collection)o), bc.getMapClassMeta(Map.class, Integer.class, gType.getElementType()), ctx); + } else { + // All other types can't be serialized as key/value pairs, so we create a + // mock key/value pair with a "_value" key. + out.append("_value="); + super.serializeAnything(out, o, null, ctx, null, null, false, true); + } + + ctx.pop(); + return out; + } catch (SerializeException e) { + throw e; + } catch (StackOverflowError e) { + throw e; + } catch (Throwable e) { + throw new SerializeException("Exception occurred trying to process object of type ''{0}''", (o == null ? null : o.getClass().getName())).initCause(e); + } + } + + private Map<Integer,Object> getCollectionMap(Collection<?> c) { + Map<Integer,Object> m = new TreeMap<Integer,Object>(); + int i = 0; + for (Object o : c) + m.put(i++, o); + return m; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private SerializerWriter serializeMap(UonSerializerWriter out, Map m, ClassMeta<?> type, UonSerializerContext ctx) throws IOException, SerializeException { + + m = sort(ctx, m); + + ClassMeta<?> keyType = type.getKeyType(), valueType = type.getValueType(); + + int depth = ctx.getIndent(); + boolean addAmp = false; + + Iterator mapEntries = m.entrySet().iterator(); + + while (mapEntries.hasNext()) { + Map.Entry e = (Map.Entry) mapEntries.next(); + Object value = e.getValue(); + Object key = generalize(ctx, e.getKey(), keyType); + + + if (shouldUseExpandedParams(value, ctx)) { + Iterator i = value instanceof Collection ? ((Collection)value).iterator() : ArrayUtils.iterator(value); + while (i.hasNext()) { + if (addAmp) + out.cr(depth).append('&'); + out.appendObject(key, false, true, true).append('='); + super.serializeAnything(out, i.next(), null, ctx, (key == null ? null : key.toString()), null, false, true); + addAmp = true; + } + } else { + if (addAmp) + out.cr(depth).append('&'); + out.appendObject(key, false, true, true).append('='); + super.serializeAnything(out, value, valueType, ctx, (key == null ? null : key.toString()), null, false, true); + addAmp = true; + } + } + + return out; + } + + @SuppressWarnings({ "rawtypes" }) + private SerializerWriter serializeBeanMap(UonSerializerWriter out, BeanMap m, boolean addClassAttr, UonSerializerContext ctx) throws IOException, SerializeException { + int depth = ctx.getIndent(); + + Iterator mapEntries = m.entrySet().iterator(); + + // Print out "_class" attribute on this bean if required. + if (addClassAttr) { + String attr = "_class"; + out.appendObject(attr, false, false, true).append('=').append(m.getClassMeta().getInnerClass().getName()); + if (mapEntries.hasNext()) + out.cr(depth).append('&'); + } + + boolean addAmp = false; + + while (mapEntries.hasNext()) { + BeanMapEntry p = (BeanMapEntry)mapEntries.next(); + BeanPropertyMeta pMeta = p.getMeta(); + + String key = p.getKey(); + Object value = null; + try { + value = p.getValue(); + } catch (StackOverflowError e) { + throw e; + } catch (Throwable t) { + ctx.addBeanGetterWarning(pMeta, t); + } + + if (canIgnoreValue(ctx, pMeta.getClassMeta(), key, value)) + continue; + + if (value != null && shouldUseExpandedParams(pMeta, ctx)) { + ClassMeta cm = pMeta.getClassMeta(); + // Filtered object array bean properties may be filtered resulting in ArrayLists, + // so we need to check type if we think it's an array. + Iterator i = (cm.isCollection() || value instanceof Collection) ? ((Collection)value).iterator() : ArrayUtils.iterator(value); + while (i.hasNext()) { + if (addAmp) + out.cr(depth).append('&'); + + out.appendObject(key, false, true, true).append('='); + + super.serializeAnything(out, i.next(), pMeta.getClassMeta().getElementType(), ctx, key, pMeta, false, true); + + addAmp = true; + } + } else { + if (addAmp) + out.cr(depth).append('&'); + + out.appendObject(key, false, true, true).append('='); + + super.serializeAnything(out, value, pMeta.getClassMeta(), ctx, key, pMeta, false, true); + + addAmp = true; + } + + } + return out; + } + + /** + * Returns true if the specified bean property should be expanded as multiple key-value pairs. + */ + private final boolean shouldUseExpandedParams(BeanPropertyMeta<?> pMeta, UonSerializerContext ctx) { + ClassMeta<?> cm = pMeta.getClassMeta(); + if (cm.isArray() || cm.isCollection()) { + if (ctx.isExpandedParams()) + return true; + if (pMeta.getBeanMeta().getClassMeta().getUrlEncodingMeta().isExpandedParams()) + return true; + } + return false; + } + + private final boolean shouldUseExpandedParams(Object value, UonSerializerContext ctx) { + if (value == null) + return false; + ClassMeta<?> cm = ctx.getBeanContext().getClassMetaForObject(value).getFilteredClassMeta(); + if (cm.isArray() || cm.isCollection()) { + if (ctx.isExpandedParams()) + return true; + } + return false; + } + + //-------------------------------------------------------------------------------- + // Methods for constructing individual parameter values. + //-------------------------------------------------------------------------------- + + /** + * Converts the specified object to a string using this serializers {@link BeanContext#convertToType(Object, Class)} method + * and runs {@link URLEncoder#encode(String,String)} against the results. + * Useful for constructing URL parts. + * + * @param o The object to serialize. + * @return The serialized object. + */ + public String serializeUrlPart(Object o) { + try { + // Shortcut for simple types. + ClassMeta<?> cm = getBeanContext().getClassMetaForObject(o); + if (cm != null) + if (cm.isCharSequence() || cm.isNumber() || cm.isBoolean()) + return o.toString(); + + UonSerializerContext uctx = createContext(null, null); + StringWriter w = new StringWriter(); + super.doSerialize(o, w, uctx); + return w.toString(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Serializer */ + public UonSerializerContext createContext(ObjectMap properties, Method javaMethod) { + return new UonSerializerContext(getBeanContext(), sp, usp, uep, properties, javaMethod); + } + + @Override /* Serializer */ + protected void doSerialize(Object o, Writer out, SerializerContext ctx) throws IOException, SerializeException { + UonSerializerContext uctx = (UonSerializerContext)ctx; + serializeAnything(uctx.getWriter(out), o, uctx); + } + + @Override /* CoreApi */ + public UrlEncodingSerializer setProperty(String property, Object value) throws LockedException { + checkLock(); + if (! usp.setProperty(property, value)) + if (! uep.setProperty(property, value)) + super.setProperty(property, value); + return this; + } + + @Override /* CoreApi */ + public UrlEncodingSerializer setProperties(ObjectMap properties) throws LockedException { + for (Map.Entry<String,Object> e : properties.entrySet()) + setProperty(e.getKey(), e.getValue()); + return this; + } + + @Override /* CoreApi */ + public UrlEncodingSerializer addNotBeanClasses(Class<?>...classes) throws LockedException { + super.addNotBeanClasses(classes); + return this; + } + + @Override /* CoreApi */ + public UrlEncodingSerializer addFilters(Class<?>...classes) throws LockedException { + super.addFilters(classes); + return this; + } + + @Override /* CoreApi */ + public <T> UrlEncodingSerializer addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException { + super.addImplClass(interfaceClass, implClass); + return this; + } + + @Override /* CoreApi */ + public UrlEncodingSerializer setClassLoader(ClassLoader classLoader) throws LockedException { + super.setClassLoader(classLoader); + return this; + } + + @Override /* Lockable */ + public UrlEncodingSerializer lock() { + super.lock(); + return this; + } + + @Override /* Lockable */ + public UrlEncodingSerializer clone() { + UrlEncodingSerializer c = (UrlEncodingSerializer)super.clone(); + c.usp = usp.clone(); + c.uep = uep.clone(); + return c; + } +}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/annotation/UrlEncoding.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/annotation/UrlEncoding.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/annotation/UrlEncoding.class new file mode 100755 index 0000000..84e2638 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/annotation/UrlEncoding.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/annotation/UrlEncoding.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/annotation/UrlEncoding.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/annotation/UrlEncoding.java new file mode 100755 index 0000000..d0870ad --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/annotation/UrlEncoding.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.urlencoding.annotation; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.*; + +import com.ibm.juno.core.urlencoding.*; + +/** + * Annotation that can be applied to classes, fields, and methods to tweak how + * they are handled by {@link UrlEncodingSerializer} and {@link UrlEncodingParser}. + * + * @author James Bognar ([email protected]) + */ +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +@Inherited +public @interface UrlEncoding { + + /** + * When true, bean properties of type array or Collection will be expanded into multiple key=value pairings. + * <p> + * This annotation is identical in behavior to using the {@link UrlEncodingProperties#URLENC_expandedParams} + * property, but applies to only instances of this bean. + */ + boolean expandedParams() default false; +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/annotation/package.html ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/annotation/package.html b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/annotation/package.html new file mode 100755 index 0000000..806eadb --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/annotation/package.html @@ -0,0 +1,34 @@ +<!DOCTYPE HTML> +<!-- + Licensed Materials - Property of IBM + (c) Copyright IBM Corporation 2015 All Rights Reserved. + + Note to U.S. Government Users Restricted Rights: + Use, duplication or disclosure restricted by GSA ADP Schedule + Contract with IBM Corp. + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>URL-Encoding annotations</p> +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/doc-files/Example_HTML.png ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/doc-files/Example_HTML.png b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/doc-files/Example_HTML.png new file mode 100755 index 0000000..ab74763 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/doc-files/Example_HTML.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/doc-files/Example_UrlEncoding.png ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/doc-files/Example_UrlEncoding.png b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/doc-files/Example_UrlEncoding.png new file mode 100755 index 0000000..34de8a7 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/doc-files/Example_UrlEncoding.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/doc-files/rfc_uon.txt ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/doc-files/rfc_uon.txt b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/doc-files/rfc_uon.txt new file mode 100755 index 0000000..c79c9c5 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/doc-files/rfc_uon.txt @@ -0,0 +1,352 @@ +Network Working Group J. Bognar +Request for Comments: 9999 C. Chaney +Category: Informational IBM + Jan 2014 + + ***DRAFT*** + URI Object Notation (UON): Generic Syntax + + +About this document + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) IBM Corp. 2014. All Rights Reserved. + +Abstract + + This document describes a grammar that builds upon RFC2396 + (Uniform Resource Identifiers). Its purpose is to define a + generalized object notation for URI query parameter values similar in + concept to Javascript Object Notation (RFC4627). The goal is a + syntax such that any data structure defined in JSON can be losslessly + defined in an equivalent URI-based grammar, yet be fully compliant + with the RFC2396 specification. + + This grammar provides the ability to construct the following data + structures in URL parameter values: + + OBJECT + ARRAY + NUMBER + BOOLEAN + STRING + NULL + + Example: + + The following shows a sample object defined in Javascript: + + var x = { + id: 1, + name: 'John Smith', + uri: 'http://sample/addressBook/person/1', + addressBookUri: 'http://sample/addressBook', + birthDate: '1946-08-12T00:00:00Z', + otherIds: null, + addresses: [ + { + uri: 'http://sample/addressBook/address/1', + personUri: 'http://sample/addressBook/person/1', + id: 1, + street: '100 Main Street', + city: 'Anywhereville', + state: 'NY', + zip: 12345, + isCurrent: true, + } + ] + } + + Using the "strict" syntax defined in this document, the equivalent + UON notation would be as follows: + + x=$o(id=$n(1),name=John+Smith,uri=http://sample/ + addressBook/person/1,addressBookUri=http://sample/ + addressBook,birthDate=1946-08-12T00:00:00Z,otherIds=%00, + addresses=$a($o(uri=http://sample/addressBook/ + address/1,personUri=http://sample/addressBook/ + person/1,id=$n(1),street=100+Main+Street,city= + Anywhereville,state=NY,zip=$n(12345),isCurrent=$b(true)))) + + A secondary "lax" syntax is available when the data type of the + values are already known on the receiving end of the transmission: + + x=(id=1,name=John+Smith,uri=http://sample/ + addressBook/person/1,addressBookUri=http://sample/ + addressBook,birthDate=1946-08-12T00:00:00Z,otherIds=%00, + addresses=((uri=http://sample/addressBook/ + address/1,personUri=http://sample/addressBook/ + person/1,id=1,street=100+Main+Street,city= + Anywhereville,state=NY,zip=12345,isCurrent=true))) + + Values represented in strict mode can be losslessly converted + back and forth into a JSON model without any additional + information. Values represented in lax mode cannot. + +1. Language constraints + + The grammar syntax is constrained to usage of characters allowed by + URI notation: + + uric = reserved | unreserved | escaped + reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | + "$" | "," + unreserved = alphanum | mark + mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" + + In particular, the URI specification disallows the following + characters in unencoded form: + + unwise = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`" + delims = "<" | ">" | "#" | "%" | <"> + + The exclusion of {} and [] characters eliminates the possibility of + using JSON as parameter values. + + +2. Grammar constructs + + The grammar consists of the following language constructs: + + Objects - Values consisting of one or more child name/value pairs. + Arrays - Values consisting of zero or more child values. + Booleans - Values consisting of true/false values. + Numbers - Decimal and floating point values. + Strings - Everything else. + +2.1. Objects + + Objects are values consisting of one or more child name/value pairs. + The $o() construct is used to define an object. + + Example: A simple map with two key/value pairs: + + a1=$o(b1=x1,b2=x2) + + Example: A nested map: + + a1=$o(b1=$o(c1=x1,c2=x2)) + + When the data type is already known to be an object on the receiving + end, then the type flag can be removed from the construct to produce + a simplified value. + + Example: A nested map using "lax" syntax: + + a1=(b1=(c1=x1,c2=x2)) + +2.2. Arrays + + Arrays are values consisting of zero or more child values. + The $a() construct is used to define an array. + + Example: An array of two string values: + + a1=$a(x1,x2) + + Example: A 2-dimensional array: + + a1=$a($a(x1,x2),$a(x3,x4)) + + Example: An array of objects: + + a1=$a($o(b1=x1,b2=x2),$o(c1=x1,c2=x2)) + + When the data type is already known to be an array on the receiving + end, then the type flag can be removed from the construct to produce + a simplified value. + + Example: An array of objects using "lax" syntax: + + a1=((b1=x1,b2=x2),(c1=x1,c2=x2)) + +2.3. Booleans + + Booleans are values that can only take on values "true" or "false". + The $b() construct is used to define a boolean. + + Example: Two boolean values: + + a1=$b(true)&a2=$b(false) + + When the data type is already known to be a boolean on the receiving + end, then the type flag and parentheses can be removed from the + construct to produce a simplified value. + + Example: Two boolean values using "lax" syntax: + + a1=true&a2=false + +2.4. Numbers + + The $n() construct is used to define a number. + Both decimal and float numbers are supported. + + Example: Two numerical values, one decimal and one float: + + a1=$n(123)&a2=$n(1.23e1) + + When the data type is already known to be a number on the receiving + end, then the type flag and parentheses can be removed from the + construct to produce a simplified value. + + Example: Two numerical values using "lax" syntax: + + a1=123&a2=1.23e1 + +2.5. Strings + + Anything not conforming to one of the constructs described above + are treated as simple strings. + + Example: A simple string value: + + a1=foobar + + The tilde character (~) is used for escaping characters to prevent + them from being confused with syntax characters. + + The following characters must be escaped in string literals: + + $ , ( ) ~ = + + For example, the string literal "$o(b1=x)" should be + represented as follows: + + a1=~$o~(b1~=x~) + + In addition, strings can optionally be enclosed in parentheses + when needed to handle ambiguous cases. + + The following two values are equivalent: + + a1=foobar + a1=(foobar) + + Using parentheses, the following construct can be used to represent + an empty string: + + a1=() + + The purpose for this is to handle a potential ambiguity in the + representation of an empty array ([]) vs. an array containing one + empty string ([""]). An array containing one empty string is + represented as follows: + + a1=$a(()) + + Without this construct, there would not be a way to tell the + difference between an empty array and an array containing an empty + string: + + a1=$a() + + Note that an array consisting of two empty strings does not suffer + from this ambiguity, and the use of parenthesis is optional in + this case: + + a1=$a(,) + +2.7. Null values + + Nulls are represented by ASCII '0' as an escaped hex sequence: + + a1=%00 + + Note that a string consisting of a single null character can be + represented with the following construct: + + a1=(%00) + +2.8. Top-level attribute names + + Top-level attribute names (e.g. "a1" in "&a1=foobar") are treated + as strings but for one exception. The '=' character must be + encoded so as not to be confused as a key/value separator. + Note that the '=' character must also be escaped per the UON + notation. + + For example, the UON equivalent of {"a=b":"a=b"} constructed as + a top-level query parameter string would be as follows: + + a~%3Db=a~=b + + Note that the '=' character is encoded in the attribute name, + but it is not necessary to have it encoded in the attribute value. + +2.9. URL-encoded characters + + UON notation allows for any character, even UON grammar + characters, to be URL-encoded. + + The following query strings are fully equivalent in structure: + + a1=$o(b1=x1,b2=x2) + %61%31=%24%6F%28%62%31%3D%78%31%2C%62%32%3D%78%32%29 + + +3. BNF + + The following BNF describes the syntax for top-level URI query + parameter values (e.g. ?<attrname>=<value>). + + attrname = (string | null) + value = (var | string | null) + + string = ("(" litchar* ")") | litchar* + null = "%00" + + var = ovar | avar | nvar | bvar + ovar = ovar_strict | ovar_lax + avar = avar_strict | avar_lax + nvar = nvar_strict | nvar_lax + bvar = bvar_strict | bvar_lax + ovar_strict = "$o(" [pairs] ")" + ovar_lax = "(" [pairs] ")" + avar_strict = "$a(" [values] ")" + avar_lax = "(" [values] ")" + nvar_strict = "$n(" number ")" + nvar_lax = number + bvar_strict = "$b(" boolean ")" + bvar_lax = boolean + + pairs = pair ["," pairs] + pair = key "=" value + values = value ["," values] + key = (string | null) + boolean = "true" | "false" + + escape_seq = "~" escaped + encode_seq = "%" digithex digithex + + number = [-] (decimal | float) [exp] + decimal = "0" | (digit19 digit*) + float = decimal "." digit+ + exp = "e" [("+" | "-")] digit+ + + litchar = unencoded | encode_seq | escape_seq + escaped = "$" | "," | "(" | ")" | "~" | "=" + unencoded = alpha | digit | + ";" | "/" | "?" | ":" | "@" | + "-" | "_" | "." | "!" | "*" | "'" + alpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | + "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | + "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | + "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | + "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | + "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" + digit = "0" | digit19 + digit19 = "1" | "2" | "3" | "4" | "5" | "6" | "7" | + "8" | "9" + digithex = digit | + "A" | "B" | "C" | "D" | "E" | "F" | + "a" | "b" | "c" | "d" | "e" | "f" + + + +
