/*
* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* This source file is part of the Cangjie project, licensed under Apache-2.0
* with Runtime Library Exception.
*
* See https://cangjie-lang.cn/pages/LICENSE for license information.
*/
/**
* @file The file declares the Form class.
*/
package stdx.encoding.url
import std.collection.*
const MAX_URL_FORM_ITEMS: Int64 = 4096
/**
* Form stores the key-value pair of the query part of the URL.
*
* @Since 0.18.6
*/
public class Form {
private var data: HashMap<String, ArrayList<String>>
private func itemCount(): Int64 {
var count = 0
for ((_, values) in data) {
count += values.size
}
return count
}
private func ensureCanAddItem(addition: Int64): Unit {
if (itemCount() + addition > MAX_URL_FORM_ITEMS) {
throw IllegalStateException("Form item count exceeds ${MAX_URL_FORM_ITEMS}.")
}
}
public init() {
this.data = HashMap<String, ArrayList<String>>()
}
/**
* Parse the URL-encoded query string and save the key-value pair.
*
* @param The query String that Separated by r'&' and r'='.
*
* @since 0.18.6
*
* @throws UrlSyntaxException if get an invalid URL escape
*/
public init(queryComponent: String) {
this()
let arr = queryComponent.split("&", removeEmpty: true)
for (query in arr) {
let kv = query.split("=", 2)
let key = decode(kv[0], isQuery: true)
let value = if (kv.size == 1) {
String.empty
} else {
decode(kv[1], isQuery: true)
}
ensureCanAddItem(1)
if (data.contains(key)) {
data[key].add(value)
} else {
data.add(key, ArrayList([value]))
}
}
}
/*
* Call the function init(), used to clone a form.
*
* @since 0.18.6
*/
private init(data: HashMap<String, ArrayList<String>>) {
this.data = data
}
/**
* Check whether the size of data is empty. If yes, true is returned. Otherwise, false is returned.
*
* @return bool if yes, true is returned. Otherwise, false is returned.
*
* @since 0.30.3
*/
public func isEmpty(): Bool {
return this.data.isEmpty()
}
/**
* Obtains the value of the key. If the key corresponds to multiple values, the first value is returned.
*
* @param key The form query key.
*
* @return Option of the first value.
*
* @since 0.18.6
*/
public func get(key: String): Option<String> {
match (data.get(key)) {
case Some(buffer) => buffer.get(0)
case None => Option<String>.None
}
}
/**
* Obtains all values corresponding to the key in the ArrayList format.
*
* @param key The form query key.
*
* @return return all value the given key.
*
* @since 0.24.4
*/
public func getAll(key: String): ArrayList<String> {
match (data.get(key)) {
case Some(buffer) => buffer.clone()
case None => ArrayList<String>()
}
}
/**
* Save the key-value pair. If the key already exists, the value will be replaced.
*
* @param key The form query key.
* @param value The form query value.
*
*
* @since 0.18.6
*/
public func set(key: String, value: String): Unit {
if (!data.contains(key)) {
ensureCanAddItem(1)
}
data.add(key, ArrayList([value]))
}
/**
* Save the key-value pair. If the key already exists,
* the values are not replaced, but are stored together in the Arraylist.
*
* @param key The form query key.
* @param value The form query value.
*
* @since 0.18.6
*/
public func add(key: String, value: String): Unit {
ensureCanAddItem(1)
if (data.contains(key)) {
data[key].add(value)
} else {
data.add(key, ArrayList([value]))
}
}
/**
* Removes key-value pairs based on key.
*
* @param key The form query key.
*
* @since 0.18.6
*/
public func remove(key: String): Unit {
data.remove(key)
}
/**
* Clone Form.
*
* @Return The clone Form instance
* @since 0.18.6
*/
public func clone(): Form {
var cloneData = HashMap<String, ArrayList<String>>()
for (i in data.keys()) {
var buffer = data[i]
cloneData.add(i, ArrayList(buffer))
}
return Form(cloneData)
}
/**
* Converting a Form to an Encoded String
*
* @Return The encoded String
* @since 0.18.6
*/
public func toEncodeString(): String {
if (!this.data.isEmpty()) {
var strEnCode = StringBuilder()
for ((k, v) in this.data) {
for (vv in v) {
strEnCode.append(encode(k, UNRESERVED, isQuery: true))
strEnCode.append(r'=')
strEnCode.append(encode(vv, UNRESERVED, isQuery: true))
strEnCode.append(r'&')
}
}
return strEnCode.toString()[..strEnCode.size - 1]
}
return ""
}
}