All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.urlencoding.UrlEncodingParser Maven / Gradle / Ivy

There is a newer version: 9.0.1
Show newest version
// ***************************************************************************************************************************
// * 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.                                              *
// ***************************************************************************************************************************
package org.apache.juneau.urlencoding;

import static org.apache.juneau.internal.ArrayUtils.*;
import static org.apache.juneau.internal.StringUtils.*;

import java.util.*;

import org.apache.juneau.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.uon.*;

/**
 * Parses URL-encoded text into POJO models.
 *
 * 
Media types:
* * Handles Content-Type types: application/x-www-form-urlencoded * *
Description:
* * Parses URL-Encoded text (e.g. "foo=bar&baz=bing") into POJOs. * *

* Expects parameter values to be in UON notation. * *

* This parser uses a state machine, which makes it very fast and efficient. */ @SuppressWarnings({ "unchecked" }) public class UrlEncodingParser extends UonParser implements PartParser { //------------------------------------------------------------------------------------------------------------------- // Configurable properties //------------------------------------------------------------------------------------------------------------------- private static final String PREFIX = "UrlEncodingParser."; /** * Parser bean property collections/arrays as separate key/value pairs ({@link Boolean}, default=false). * *

* This is the parser-side equivalent of the {@link #URLENC_expandedParams} setting. * *

* This option only applies to beans. * *

Notes:
*
    *
  • If parsing multi-part parameters, it's highly recommended to use Collections or Lists * as bean property types instead of arrays since arrays have to be recreated from scratch every time a value * is added to it. *
*/ public static final String URLENC_expandedParams = PREFIX + "expandedParams"; //------------------------------------------------------------------------------------------------------------------- // Predefined instances //------------------------------------------------------------------------------------------------------------------- /** Reusable instance of {@link UrlEncodingParser}. */ public static final UrlEncodingParser DEFAULT = new UrlEncodingParser(PropertyStore.create()); //------------------------------------------------------------------------------------------------------------------- // Instance //------------------------------------------------------------------------------------------------------------------- private final UrlEncodingParserContext ctx; /** * Constructor. * * @param propertyStore The property store containing all the settings for this object. */ public UrlEncodingParser(PropertyStore propertyStore) { super(propertyStore.copy().append(UON_decodeChars, true), "application/x-www-form-urlencoded"); this.ctx = createContext(UrlEncodingParserContext.class); } @Override /* CoreObject */ public UrlEncodingParserBuilder builder() { return new UrlEncodingParserBuilder(propertyStore); } /** * Parse a URL query string into a simple map of key/value pairs. * * @param qs The query string to parse. * @param map The map to parse into. If null, then a new {@link TreeMap} will be used. * @return A sorted {@link TreeMap} of query string entries. * @throws Exception */ public Map parseIntoSimpleMap(String qs, Map map) throws Exception { Map m = map == null ? new TreeMap() : map; if (isEmpty(qs)) return m; // We're reading from a string, so we don't need to make sure close() is called on the pipe. ParserPipe p = new ParserPipe(qs, false, false, null, null); UonReader r = new UonReader(p, true); final int S1=1; // Looking for attrName start. final int S2=2; // Found attrName start, looking for = or & or end. final int S3=3; // Found =, looking for valStart. final int S4=4; // Found valStart, looking for & or end. try { int c = r.peekSkipWs(); if (c == '?') r.read(); int state = S1; String currAttr = null; while (c != -1) { c = r.read(); if (state == S1) { if (c != -1) { r.unread(); r.mark(); state = S2; } } else if (state == S2) { if (c == -1) { add(m, r.getMarked(), null); } else if (c == '\u0001') { m.put(r.getMarked(0,-1), null); state = S1; } else if (c == '\u0002') { currAttr = r.getMarked(0,-1); state = S3; } } else if (state == S3) { if (c == -1 || c == '\u0001') { add(m, currAttr, ""); } else { if (c == '\u0002') r.replace('='); r.unread(); r.mark(); state = S4; } } else if (state == S4) { if (c == -1) { add(m, currAttr, r.getMarked()); } else if (c == '\u0001') { add(m, currAttr, r.getMarked(0,-1)); state = S1; } else if (c == '\u0002') { r.replace('='); } } } } finally { r.close(); } return m; } private static void add(Map m, String key, String val) { boolean b = m.containsKey(key); if (val == null) { if (! b) m.put(key, null); } else if (b && m.get(key) != null) { m.put(key, append(m.get(key), val)); } else { m.put(key, new String[]{val}); } } @Override /* PartParser */ public T parse(PartType partType, String in, ClassMeta type) throws ParseException { if (in == null) return null; if (type.isString() && in.length() > 0) { // Shortcut - If we're returning a string and the value doesn't start with "'" or is "null", then // just return the string since it's a plain value. // This allows us to bypass the creation of a UonParserSession object. char x = firstNonWhitespaceChar(in); if (x != '\'' && x != 'n' && in.indexOf('~') == -1) return (T)in; if (x == 'n' && "null".equals(in)) return null; } UonParserSession session = createParameterSession(); ParserPipe pipe = session.createPipe(in); try { UonReader r = session.getUonReader(pipe, false); return session.parseAnything(type, r, null, true, null); } catch (ParseException e) { throw e; } catch (Exception e) { throw new ParseException(session.getLastLocation(), e); } finally { pipe.close(); session.close(); } } //-------------------------------------------------------------------------------- // Entry point methods //-------------------------------------------------------------------------------- @Override /* Parser */ public UrlEncodingParserSession createSession(ParserSessionArgs args) { return new UrlEncodingParserSession(ctx, args); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy