001/*
002 * Copyright © 2025 CUI-OpenSource-Software (info@cuioss.de)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package de.cuioss.http.security.data;
017
018import de.cuioss.http.security.core.ValidationType;
019import org.jspecify.annotations.Nullable;
020
021/**
022 * Immutable record representing a URL query parameter with name and value.
023 *
024 * <p>This record encapsulates the key-value pair structure of URL query parameters,
025 * providing a type-safe way to handle parameter data in HTTP security validation.</p>
026 *
027 * <h3>Design Principles</h3>
028 * <ul>
029 *   <li><strong>Immutability</strong> - All fields are final and the record cannot be modified</li>
030 *   <li><strong>Type Safety</strong> - Strongly typed representation of parameter data</li>
031 *   <li><strong>Null Safety</strong> - Explicit handling of null values with clear semantics</li>
032 *   <li><strong>Value Semantics</strong> - Records provide automatic equals/hashCode/toString</li>
033 * </ul>
034 *
035 * <h3>Usage Examples</h3>
036 * <pre>
037 * // Create a parameter
038 * URLParameter param = new URLParameter("userId", "12345");
039 *
040 * // Access components
041 * String name = param.name();     // "userId"
042 * String value = param.value();   // "12345"
043 *
044 * // Use in validation
045 * validator.validate(param.name(), ValidationType.PARAMETER_NAME);
046 * validator.validate(param.value(), ValidationType.PARAMETER_VALUE);
047 *
048 * // Parameters are value objects
049 * URLParameter param2 = new URLParameter("userId", "12345");
050 * assert param.equals(param2);  // true
051 * </pre>
052 *
053 * <h3>Null Handling</h3>
054 * <p>Both name and value can be null to represent edge cases in HTTP parsing,
055 * though null parameter names are typically invalid in well-formed URLs.</p>
056 *
057 * <h3>Security Considerations</h3>
058 * <p>This record is a simple data container. Security validation should be applied
059 * to the name and value components separately using appropriate validators for
060 * {@link ValidationType#PARAMETER_NAME} and {@link ValidationType#PARAMETER_VALUE}.</p>
061 *
062 * Implements: Task B3 from HTTP verification specification
063 *
064 * @param name The parameter name (e.g., "userId", "page", "filter")
065 * @param value The parameter value (e.g., "12345", "admin", "active")
066 *
067 * @since 1.0
068 * @see ValidationType#PARAMETER_NAME
069 * @see ValidationType#PARAMETER_VALUE
070 */
071public record URLParameter(@Nullable String name, @Nullable String value) {
072
073    /**
074     * Creates a URLParameter with empty value.
075     * Useful for parameters that appear without values (e.g., "?flag" instead of "?flag=value").
076     *
077     * @param name The parameter name, should not be null
078     * @return A URLParameter with the specified name and empty string value
079     */
080    public static URLParameter withEmptyValue(String name) {
081        return new URLParameter(name, "");
082    }
083
084    /**
085     * Checks if this parameter has a non-null, non-empty name.
086     *
087     * @return true if the name is not null and not empty
088     */
089    public boolean hasName() {
090        return name != null && !name.isEmpty();
091    }
092
093    /**
094     * Checks if this parameter has a non-null, non-empty value.
095     *
096     * @return true if the value is not null and not empty
097     */
098    public boolean hasValue() {
099        return value != null && !value.isEmpty();
100    }
101
102    /**
103     * Checks if this parameter represents a flag (has name but no meaningful value).
104     * A parameter is considered a flag if it has a name but the value is null or empty.
105     *
106     * @return true if this appears to be a flag parameter
107     */
108    public boolean isFlag() {
109        return hasName() && (value == null || value.isEmpty());
110    }
111
112    /**
113     * Returns the parameter name, or a default value if the name is null.
114     *
115     * @param defaultName The default name to return if name is null
116     * @return The parameter name or the default
117     */
118    public String nameOrDefault(String defaultName) {
119        return name != null ? name : defaultName;
120    }
121
122    /**
123     * Returns the parameter value, or a default value if the value is null.
124     *
125     * @param defaultValue The default value to return if value is null
126     * @return The parameter value or the default
127     */
128    public String valueOrDefault(String defaultValue) {
129        return value != null ? value : defaultValue;
130    }
131
132    /**
133     * Returns a string representation suitable for URL encoding.
134     * Note: This does not perform actual URL encoding - use appropriate
135     * encoding utilities for that purpose.
136     *
137     * @return A string in the format "name=value" or "name" for flag parameters
138     */
139    public String toParameterString() {
140        if (name == null) {
141            return value != null ? "=" + value : "";
142        }
143        if (value == null || value.isEmpty()) {
144            return name;
145        }
146        return name + "=" + value;
147    }
148
149    public URLParameter withName(String newName) {
150        return new URLParameter(newName, value);
151    }
152
153    public URLParameter withValue(String newValue) {
154        return new URLParameter(name, newValue);
155    }
156}