1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.metapath.item.atomic.impl;
7   
8   import java.util.regex.Pattern;
9   
10  import dev.metaschema.core.metapath.item.atomic.AbstractAnyAtomicItem;
11  import dev.metaschema.core.metapath.item.atomic.IStringItem;
12  import dev.metaschema.core.metapath.item.function.IMapKey;
13  import dev.metaschema.core.metapath.item.function.impl.AbstractStringMapKey;
14  import edu.umd.cs.findbugs.annotations.NonNull;
15  
16  /**
17   * A common base class for all items derived from {@link IStringItem}.
18   */
19  public abstract class AbstractStringItem
20      extends AbstractAnyAtomicItem<String>
21      implements IStringItem {
22    private static final String WHITESPACE_SEGMENT = "[ \t\r\n]";
23    /**
24     * Pattern to match one or more whitespace characters at the end of a string.
25     */
26    private static final Pattern TRIM_END = Pattern.compile(WHITESPACE_SEGMENT + "++$");
27    /**
28     * Pattern to match one or more whitespace characters at the start of a string.
29     */
30    private static final Pattern TRIM_START = Pattern.compile("^" + WHITESPACE_SEGMENT + "+");
31    /**
32     * Pattern to match two or more consecutive whitespace characters.
33     */
34    private static final Pattern TRIM_MIDDLE = Pattern.compile(WHITESPACE_SEGMENT + "{2,}");
35  
36    /**
37     * Construct a new string item with the provided {@code value}.
38     *
39     * @param value
40     *          the value to wrap
41     */
42    protected AbstractStringItem(@NonNull String value) {
43      super(value);
44    }
45  
46    @Override
47    public String asString() {
48      return getValue();
49    }
50  
51    @Override
52    public IMapKey asMapKey() {
53      return new MapKey();
54    }
55  
56    @Override
57    public IStringItem normalizeSpace() {
58      String value = asString();
59      value = TRIM_START.matcher(value).replaceFirst("");
60      value = TRIM_MIDDLE.matcher(value).replaceAll(" ");
61      value = TRIM_END.matcher(value).replaceFirst("");
62      assert value != null;
63  
64      return IStringItem.valueOf(value);
65    }
66  
67    @Override
68    protected String getValueSignature() {
69      return "'" + getValue() + "'";
70    }
71  
72    @Override
73    public int hashCode() {
74      return asString().hashCode();
75    }
76  
77    @Override
78    public boolean equals(Object obj) {
79      return this == obj
80          || obj instanceof IStringItem && compareTo((IStringItem) obj) == 0;
81    }
82  
83    private final class MapKey
84        extends AbstractStringMapKey {
85  
86      @Override
87      public IStringItem getKey() {
88        return AbstractStringItem.this;
89      }
90  
91      @Override
92      public String asString() {
93        return getKey().asString();
94      }
95    }
96  
97  }