com.helger.web.fileupload.parse.ParameterParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ph-web Show documentation
Show all versions of ph-web Show documentation
Java library with basic web functionality
/*
* Copyright (C) 2014-2024 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* 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.helger.web.fileupload.parse;
import java.util.Locale;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import com.helger.commons.annotation.ReturnsMutableCopy;
import com.helger.commons.collection.ArrayHelper;
import com.helger.commons.collection.impl.CommonsHashMap;
import com.helger.commons.collection.impl.ICommonsMap;
import com.helger.commons.string.StringHelper;
/**
* A simple parser intended to parse sequences of name/value pairs. Parameter
* values are expected to be enclosed in quotes if they contain unsafe
* characters, such as '=' characters or separators. Parameter values are
* optional and can be omitted.
*
* param1 = value; param2 = "anything goes; really"; param3
*
*
* @author Oleg Kalnichevski
*/
@NotThreadSafe
public class ParameterParser
{
/**
* String to be parsed.
*/
private char [] m_aChars;
/**
* Current position in the string.
*/
private int m_nPos = 0;
/**
* Maximum position in the string.
*/
private int m_nLen = 0;
/**
* Start of a token.
*/
private int m_nIndex1 = 0;
/**
* End of a token.
*/
private int m_nIndex2 = 0;
/**
* Whether names stored in the map should be converted to lower case.
*/
private boolean m_bLowerCaseNames = false;
/**
* Default ParameterParser constructor.
*/
public ParameterParser ()
{}
/**
* Are there any characters left to parse?
*
* @return true
if there are unparsed characters,
* false
otherwise.
*/
private boolean _hasChar ()
{
return m_nPos < m_nLen;
}
/**
* A helper method to process the parsed token. This method removes leading
* and trailing blanks as well as enclosing quotation marks, when necessary.
*
* @param bQuoted
* true
if quotation marks are expected,
* false
otherwise.
* @return the token
*/
@Nullable
private String _getToken (final boolean bQuoted)
{
// Trim leading white spaces
while (m_nIndex1 < m_nIndex2 && Character.isWhitespace (m_aChars[m_nIndex1]))
{
m_nIndex1++;
}
// Trim trailing white spaces
while (m_nIndex2 > m_nIndex1 && Character.isWhitespace (m_aChars[m_nIndex2 - 1]))
{
m_nIndex2--;
}
// Strip away quotation marks if necessary
if (bQuoted)
{
if ((m_nIndex2 - m_nIndex1) >= 2 && m_aChars[m_nIndex1] == '"' && m_aChars[m_nIndex2 - 1] == '"')
{
m_nIndex1++;
m_nIndex2--;
}
}
String ret = null;
if (m_nIndex2 > m_nIndex1)
{
ret = new String (m_aChars, m_nIndex1, m_nIndex2 - m_nIndex1);
}
return ret;
}
/**
* Parses out a token until any of the given terminators is encountered.
*
* @param cTerminator1
* the first terminating character. Any when encountered signify the
* end of the token
* @param cTerminator2
* the second terminating character. Any when encountered signify the
* end of the token
* @return the token
*/
@Nullable
private String _parseToken (final char cTerminator1, final char cTerminator2)
{
char ch;
m_nIndex1 = m_nPos;
m_nIndex2 = m_nPos;
while (_hasChar ())
{
ch = m_aChars[m_nPos];
if (ch == cTerminator1 || ch == cTerminator2)
break;
m_nIndex2++;
m_nPos++;
}
return _getToken (false);
}
/**
* Parses out a token until any of the given terminators is encountered
* outside the quotation marks.
*
* @param cTerminator
* the terminating character. Any of these characters when encountered
* outside the quotation marks signify the end of the token
* @return the token
*/
@Nullable
private String _parseQuotedToken (final char cTerminator)
{
char ch;
m_nIndex1 = m_nPos;
m_nIndex2 = m_nPos;
boolean bQuoted = false;
boolean bCharEscaped = false;
while (_hasChar ())
{
ch = m_aChars[m_nPos];
if (!bQuoted && cTerminator == ch)
break;
if (!bCharEscaped && ch == '"')
bQuoted = !bQuoted;
bCharEscaped = (!bCharEscaped && ch == '\\');
m_nIndex2++;
m_nPos++;
}
return _getToken (true);
}
/**
* Returns true
if parameter names are to be converted to lower
* case when name/value pairs are parsed.
*
* @return true
if parameter names are to be converted to lower
* case when name/value pairs are parsed. Otherwise returns
* false
*/
public boolean isLowerCaseNames ()
{
return m_bLowerCaseNames;
}
/**
* Sets the flag if parameter names are to be converted to lower case when
* name/value pairs are parsed.
*
* @param bLowerCaseNames
* true
if parameter names are to be converted to lower
* case when name/value pairs are parsed. false
otherwise.
* @return this
*/
@Nonnull
public ParameterParser setLowerCaseNames (final boolean bLowerCaseNames)
{
m_bLowerCaseNames = bLowerCaseNames;
return this;
}
/**
* Extracts a map of name/value pairs from the given string. Names are
* expected to be unique. Multiple separators may be specified and the
* earliest found in the input string is used.
*
* @param sStr
* the string that contains a sequence of name/value pairs
* @param aSeparators
* the name/value pairs separators
* @return a map of name/value pairs
*/
@Nonnull
@ReturnsMutableCopy
public ICommonsMap parse (@Nullable final String sStr, @Nullable final char [] aSeparators)
{
if (ArrayHelper.isEmpty (aSeparators))
return new CommonsHashMap <> ();
char cSep = aSeparators[0];
if (sStr != null)
{
// Find the first separator to use
int nFirstIndex = sStr.length ();
for (final char cSep2 : aSeparators)
{
final int nCurIndex = sStr.indexOf (cSep2);
if (nCurIndex != -1 && nCurIndex < nFirstIndex)
{
nFirstIndex = nCurIndex;
cSep = cSep2;
}
}
}
return parse (sStr, cSep);
}
/**
* Extracts a map of name/value pairs from the given string. Names are
* expected to be unique.
*
* @param sStr
* the string that contains a sequence of name/value pairs
* @param cSeparator
* the name/value pairs separator
* @return a map of name/value pairs
*/
@Nonnull
@ReturnsMutableCopy
public ICommonsMap parse (@Nullable final String sStr, final char cSeparator)
{
final ICommonsMap ret = new CommonsHashMap <> ();
if (sStr != null)
{
final char [] aChars = sStr.toCharArray ();
m_aChars = aChars;
m_nPos = 0;
m_nLen = sStr.length ();
while (_hasChar ())
{
String sParamName = _parseToken ('=', cSeparator);
String sParamValue = null;
if (_hasChar () && aChars[m_nPos] == '=')
{
// skip '='
m_nPos++;
sParamValue = _parseQuotedToken (cSeparator);
}
if (_hasChar () && aChars[m_nPos] == cSeparator)
{
// skip separator
m_nPos++;
}
if (StringHelper.hasText (sParamName))
{
if (m_bLowerCaseNames)
sParamName = sParamName.toLowerCase (Locale.US);
ret.put (sParamName, sParamValue);
}
}
}
return ret;
}
}