content: /*
   ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
   ~                                                                               ~
   ~ The MIT License (MIT)                                                         ~
   ~                                                                               ~
   ~ Copyright (c) 2015-2025 miaixz.org and other contributors.                    ~
   ~                                                                               ~
   ~ Permission is hereby granted, free of charge, to any person obtaining a copy  ~
   ~ of this software and associated documentation files (the "Software"), to deal ~
   ~ in the Software without restriction, including without limitation the rights  ~
   ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell     ~
   ~ copies of the Software, and to permit persons to whom the Software is         ~
   ~ furnished to do so, subject to the following conditions:                      ~
   ~                                                                               ~
   ~ The above copyright notice and this permission notice shall be included in    ~
   ~ all copies or substantial portions of the Software.                           ~
   ~                                                                               ~
   ~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR    ~
   ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,      ~
   ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE   ~
   ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER        ~
   ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ~
   ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN     ~
   ~ THE SOFTWARE.                                                                 ~
   ~                                                                               ~
   ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
  */
  package org.miaixz.bus.core.net.url;
  
  import java.nio.charset.Charset;
  import java.util.LinkedList;
  import java.util.List;
  
  import org.miaixz.bus.core.lang.Assert;
  import org.miaixz.bus.core.lang.Normal;
  import org.miaixz.bus.core.lang.Symbol;
  import org.miaixz.bus.core.text.CharsBacker;
  import org.miaixz.bus.core.xyz.CollKit;
  import org.miaixz.bus.core.xyz.ListKit;
  import org.miaixz.bus.core.xyz.ObjectKit;
  import org.miaixz.bus.core.xyz.StringKit;
  
  /**
   * URL中Path部分的封装
   *
   * @author Kimi Liu
   * @since Java 17+
   */
  public class UrlPath {
  
      private List<CharSequence> segments;
      private boolean withEngTag;
  
      /**
       * 构建UrlPath
       *
       * @return UrlPath
       */
      public static UrlPath of() {
          return new UrlPath();
      }
  
      /**
       * 构建UrlPath
       *
       * @param pathStr 初始化的路径字符串
       * @param charset decode用的编码，null表示不做decode
       * @return UrlPath
       */
      public static UrlPath of(final CharSequence pathStr, final Charset charset) {
          return of().parse(pathStr, charset);
      }
  
      /**
       * 是否path的末尾加 /
       *
       * @param withEngTag 是否path的末尾加 /
       * @return this
       */
      public UrlPath setWithEndTag(final boolean withEngTag) {
          this.withEngTag = withEngTag;
          return this;
      }
  
      /**
       * 获取path的节点列表，如果列表为空，返回{@link ListKit#empty()}
       *
       * @return 节点列表
       */
      public List<CharSequence> getSegments() {
          return ObjectKit.defaultIfNull(this.segments, ListKit.empty());
      }
  
      /**
       * 获得指定节点
       *
       * @param index 节点位置
       * @return 节点，无节点或者越界返回null
       */
      public CharSequence getSegment(final int index) {
          if (null == this.segments || index >= this.segments.size()) {
              return null;
          }
          return this.segments.get(index);
      }
  
      /**
       * 添加到path最后面
       *
       * @param segment Path节点
       * @return this
       */
      public UrlPath add(final CharSequence segment) {
          addInternal(fixPath(segment), false);
          return this;
      }
  
      /**
       * 添加到path最前面
       *
       * @param segment Path节点
       * @return this
       */
      public UrlPath addBefore(final CharSequence segment) {
          addInternal(fixPath(segment), true);
          return this;
      }
  
      /**
       * 解析path
       *
       * @param path    路径，类似于aaa/bb/ccc或/aaa/bbb/ccc
       * @param charset decode编码，null表示不解码
       * @return this
       */
      public UrlPath parse(CharSequence path, final Charset charset) {
          if (StringKit.isNotEmpty(path)) {
              // 原URL中以/结尾，则这个规则需保留
              if (StringKit.endWith(path, Symbol.C_SLASH)) {
                  this.withEngTag = true;
              }
  
              path = fixPath(path);
              if (StringKit.isNotEmpty(path)) {
                  final List<String> split = CharsBacker.split(path, Symbol.SLASH);
                  for (final String seg : split) {
                      addInternal(UrlDecoder.decodeForPath(seg, charset), false);
                  }
              }
          }
  
          return this;
      }
  
      /**
       * 构建path，前面带'/'
       * 
       * <pre>
       * path = path - abempty / path - absolute / path - noscheme / path - rootless / path - empty
       * </pre>
       *
       * @param charset encode编码，null表示不做encode
       * @return 如果没有任何内容，则返回空字符串""
       */
      public String build(final Charset charset) {
          if (CollKit.isEmpty(this.segments)) {
              // 没有节点的path取决于是否末尾追加/，如果不追加返回空串，否则返回/
              return withEngTag ? Symbol.SLASH : Normal.EMPTY;
          }
  
          final StringBuilder builder = new StringBuilder();
          for (final CharSequence segment : segments) {
              // https://www.ietf.org/rfc/rfc3986.html#section-3.3
              // 此处Path中是允许有`:`的，之前理解有误，应该是相对URI的第一个segment中不允许有`:`
              builder.append(Symbol.C_SLASH).append(RFC3986.SEGMENT.encode(segment, charset));
          }
  
          if (withEngTag) {
              if (StringKit.isEmpty(builder)) {
                  // 空白追加是保证以/开头
                  builder.append(Symbol.C_SLASH);
              } else if (!StringKit.endWith(builder, Symbol.C_SLASH)) {
                  // 尾部没有/则追加，否则不追加
                  builder.append(Symbol.C_SLASH);
              }
          }
  
          return builder.toString();
      }
  
      @Override
      public String toString() {
          return build(null);
      }
  
      /**
       * 增加节点
       *
       * @param segment 节点
       * @param before  是否在前面添加
       */
      private void addInternal(final CharSequence segment, final boolean before) {
          if (this.segments == null) {
              this.segments = new LinkedList<>();
          }
  
          if (before) {
              this.segments.add(0, segment);
          } else {
              this.segments.add(segment);
          }
      }
  
      /**
       * 修正路径，包括去掉前后的/，去掉空白符
       *
       * @param path 节点或路径path
       * @return 修正后的路径
       */
      private static String fixPath(final CharSequence path) {
          Assert.notNull(path, "Path segment must be not null!");
          if ("/".contentEquals(path)) {
              return Normal.EMPTY;
          }
  
          String segmentStr = StringKit.trim(path);
          segmentStr = StringKit.removePrefix(segmentStr, Symbol.SLASH);
          segmentStr = StringKit.removeSuffix(segmentStr, Symbol.SLASH);
          segmentStr = StringKit.trim(segmentStr);
          return segmentStr;
      }
  
  }
  