001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.urlencoding;
014
015import org.apache.juneau.*;
016import org.apache.juneau.parser.*;
017import org.apache.juneau.uon.*;
018import org.apache.juneau.urlencoding.annotation.*;
019
020/**
021 * Parses URL-encoded text into POJO models.
022 * 
023 * <h5 class='topic'>Media types</h5>
024 * 
025 * Handles <code>Content-Type</code> types:  <code><b>application/x-www-form-urlencoded</b></code>
026 * 
027 * <h5 class='topic'>Description</h5>
028 * 
029 * Parses URL-Encoded text (e.g. <js>"foo=bar&amp;baz=bing"</js>) into POJOs.
030 * 
031 * <p>
032 * Expects parameter values to be in UON notation.
033 * 
034 * <p>
035 * This parser uses a state machine, which makes it very fast and efficient.
036 */
037public class UrlEncodingParser extends UonParser {
038
039   //-------------------------------------------------------------------------------------------------------------------
040   // Configurable properties
041   //-------------------------------------------------------------------------------------------------------------------
042
043   private static final String PREFIX = "UrlEncodingParser.";
044
045   /**
046    * Configuration property:  Parser bean property collections/arrays as separate key/value pairs.
047    * 
048    * <h5 class='section'>Property:</h5>
049    * <ul>
050    *    <li><b>Name:</b>  <js>"UrlEncodingParser.expandedParams.b"</js>
051    *    <li><b>Data type:</b>  <code>Boolean</code>
052    *    <li><b>Default:</b>  <jk>false</jk>
053    *    <li><b>Session-overridable:</b>  <jk>true</jk>
054    *    <li><b>Methods:</b> 
055    *       <ul>
056    *          <li class='jm'>{@link UrlEncodingParserBuilder#expandedParams(boolean)}
057    *          <li class='jm'>{@link UrlEncodingParserBuilder#expandedParams()}
058    *          <li class='ja'>{@link UrlEncoding#expandedParams()}
059    *       </ul>
060    * </ul>
061    * 
062    * <h5 class='section'>Description:</h5>
063    * <p>
064    * This is the parser-side equivalent of the {@link #URLENC_expandedParams} setting.
065    * 
066    * <p>
067    * If <jk>false</jk>, serializing the array <code>[1,2,3]</code> results in <code>?key=$a(1,2,3)</code>.
068    * <br>If <jk>true</jk>, serializing the same array results in <code>?key=1&amp;key=2&amp;key=3</code>.
069    * 
070    * <h5 class='section'>Example:</h5>
071    * <p class='bcode'>
072    *    <jk>public class</jk> A {
073    *       <jk>public</jk> String[] f1;
074    *       <jk>public</jk> List&lt;String&gt; f2;
075    *    }
076    * 
077    *    UrlEncodingParser p1 = UrlEncodingParser.<jsf>DEFAULT</jsf>;
078    *    UrlEncodingParser p2 = UrlEncodingParser.<jsm>create</jsm>().expandedParams().build();
079    *    
080    *    A a1 = p1.parse(<js>"f1=@(a,b)&amp;f2=@(c,d)"</js>, A.<jk>class</jk>); 
081    *    
082    *    A a2 = p2.parse(<js>"f1=a&amp;f1=b&amp;f2=c&amp;f2=d"</js>, A.<jk>class</jk>); 
083    * </p>
084    * 
085    * <p>
086    * This option only applies to beans.
087    * 
088    * <h5 class='section'>Notes:</h5>
089    * <ul class='spaced-list'>
090    *    <li>
091    *       If parsing multi-part parameters, it's highly recommended to use Collections or Lists
092    *       as bean property types instead of arrays since arrays have to be recreated from scratch every time a value
093    *       is added to it.
094    * </ul>
095    */
096   public static final String URLENC_expandedParams = PREFIX + "expandedParams.b";
097
098
099   //-------------------------------------------------------------------------------------------------------------------
100   // Predefined instances
101   //-------------------------------------------------------------------------------------------------------------------
102
103   /** Reusable instance of {@link UrlEncodingParser}. */
104   public static final UrlEncodingParser DEFAULT = new UrlEncodingParser(PropertyStore.DEFAULT);
105
106
107   //-------------------------------------------------------------------------------------------------------------------
108   // Instance
109   //-------------------------------------------------------------------------------------------------------------------
110
111   final boolean
112      expandedParams;
113
114   /**
115    * Constructor.
116    * 
117    * @param ps The property store containing all the settings for this object.
118    */
119   public UrlEncodingParser(PropertyStore ps) {
120      super(
121         ps.builder()
122            .set(UON_decoding, true)
123            .build(), 
124         "application/x-www-form-urlencoded"
125      );
126      expandedParams = getBooleanProperty(URLENC_expandedParams, false);
127   }
128
129   @Override /* Context */
130   public UrlEncodingParserBuilder builder() {
131      return new UrlEncodingParserBuilder(getPropertyStore());
132   }
133
134   /**
135    * Instantiates a new clean-slate {@link UrlEncodingParserBuilder} object.
136    * 
137    * <p>
138    * This is equivalent to simply calling <code><jk>new</jk> UrlEncodingParserBuilder()</code>.
139    * 
140    * <p>
141    * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies 
142    * the settings of the object called on.
143    * 
144    * @return A new {@link UrlEncodingParserBuilder} object.
145    */
146   public static UrlEncodingParserBuilder create() {
147      return new UrlEncodingParserBuilder();
148   }
149
150   
151   //--------------------------------------------------------------------------------
152   // Entry point methods
153   //--------------------------------------------------------------------------------
154
155   @Override /* Parser */
156   public UrlEncodingParserSession createSession(ParserSessionArgs args) {
157      return new UrlEncodingParserSession(this, args);
158   }
159   
160   @Override /* Context */
161   public ObjectMap asMap() {
162      return super.asMap()
163         .append("UrlEncodingParser", new ObjectMap()
164            .append("expandedParams", expandedParams)
165         );
166   }
167}