1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.metapath.item.atomic;
7   
8   import dev.metaschema.core.metapath.function.InvalidValueForCastFunctionException;
9   import dev.metaschema.core.metapath.type.IAtomicOrUnionType;
10  import dev.metaschema.core.metapath.type.InvalidTypeMetapathException;
11  import dev.metaschema.core.metapath.type.impl.TypeConstants;
12  import edu.umd.cs.findbugs.annotations.NonNull;
13  import inet.ipaddr.IPAddress;
14  
15  /**
16   * An atomic Metapath item representing an IP address data value.
17   */
18  // FIXME: Should this be a subtype of IStringItem?
19  public interface IIPAddressItem extends IAnyAtomicItem {
20    /**
21     * Get the type information for this item.
22     *
23     * @return the type information
24     */
25    @NonNull
26    static IAtomicOrUnionType<IIPAddressItem> type() {
27      return TypeConstants.IP_ADDRESS_TYPE;
28    }
29  
30    @Override
31    default IAtomicOrUnionType<? extends IIPAddressItem> getType() {
32      return type();
33    }
34  
35    /**
36     * Get the "wrapped" IP address value.
37     *
38     * @return the underlying IP address value
39     */
40    @NonNull
41    IPAddress asIpAddress();
42  
43    /**
44     * Compares this value with the argument.
45     *
46     * @param item
47     *          the item to compare with this value
48     * @return a negative integer, zero, or a positive integer if this value is less
49     *         than, equal to, or greater than the {@code item}.
50     */
51    default int compareTo(IIPAddressItem item) {
52      return asIpAddress().compareTo(item.asIpAddress());
53    }
54  
55    /**
56     * Cast the provided type to this item type.
57     *
58     * @param item
59     *          the item to cast
60     * @return the original item if it is already this type, otherwise a new item
61     *         cast to this type
62     * @throws InvalidValueForCastFunctionException
63     *           if the provided {@code item} cannot be cast to this type
64     */
65    @NonNull
66    static IIPAddressItem cast(@NonNull IAnyAtomicItem item) {
67      IIPAddressItem retval;
68      if (item instanceof IIPAddressItem) {
69        retval = (IIPAddressItem) item;
70      } else {
71        String value;
72        try {
73          value = item.asString();
74        } catch (IllegalStateException ex) {
75          // asString can throw IllegalStateException exception
76          throw new InvalidValueForCastFunctionException(ex);
77        }
78  
79        try {
80          // try a v6 address
81          retval = IIPv6AddressItem.valueOf(value);
82        } catch (InvalidTypeMetapathException ex) {
83          // try a v4 address
84          try {
85            retval = IIPv4AddressItem.valueOf(value);
86          } catch (InvalidTypeMetapathException ex2) {
87            InvalidValueForCastFunctionException newEx = new InvalidValueForCastFunctionException(
88                String.format("The value '%s' of type '%s' is not an internet protocol address.",
89                    value,
90                    item.getJavaTypeAdapter().getPreferredName()),
91                ex2);
92            newEx.addSuppressed(ex);
93            throw newEx;
94          }
95        }
96      }
97      return retval;
98    }
99  }