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 java.net.URI;
9   
10  import dev.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider;
11  import dev.metaschema.core.metapath.function.InvalidValueForCastFunctionException;
12  import dev.metaschema.core.metapath.item.atomic.impl.AnyUriItemImpl;
13  import dev.metaschema.core.metapath.type.IAtomicOrUnionType;
14  import dev.metaschema.core.metapath.type.InvalidTypeMetapathException;
15  import dev.metaschema.core.util.ObjectUtils;
16  import edu.umd.cs.findbugs.annotations.NonNull;
17  
18  /**
19   * An atomic Metapath item containing a URI data value.
20   */
21  public interface IAnyUriItem extends IAnyAtomicItem {
22    /**
23     * Get the type information for this item.
24     *
25     * @return the type information
26     */
27    @NonNull
28    static IAtomicOrUnionType<IAnyUriItem> type() {
29      return MetaschemaDataTypeProvider.URI.getItemType();
30    }
31  
32    @Override
33    default IAtomicOrUnionType<? extends IAnyUriItem> getType() {
34      return type();
35    }
36  
37    /**
38     * Construct a new URI item using the provided string {@code value}.
39     *
40     * @param value
41     *          a string representing a URI
42     * @return the new item
43     * @throws InvalidTypeMetapathException
44     *           if the given string violates RFC2396
45     */
46    @NonNull
47    static IAnyUriItem valueOf(@NonNull String value) {
48      try {
49        return valueOf(MetaschemaDataTypeProvider.URI.parse(value));
50      } catch (IllegalArgumentException ex) {
51        throw new InvalidTypeMetapathException(
52            null,
53            String.format("Invalid URI value '%s'. %s",
54                value,
55                ex.getLocalizedMessage()),
56            ex);
57      }
58    }
59  
60    /**
61     * Construct a new URI item using the provided URI {@code value}.
62     * <p>
63     * Example usage:
64     *
65     * <pre>
66     * URI uri = URI.create("http://example.com");
67     * IAnyUriItem item = IAnyUriItem.valueOf(uri);
68     * </pre>
69     *
70     * @param value
71     *          a URI
72     * @return the new item as a {@link IAnyUriItem} if the URI is absolute or
73     *         opaque, otherwise as an {@link IUriReferenceItem}
74     */
75    @NonNull
76    static IAnyUriItem valueOf(@NonNull URI value) {
77      IAnyUriItem retval;
78      if (value.isAbsolute() || value.isOpaque()) {
79        retval = new AnyUriItemImpl(value);
80      } else {
81        retval = IUriReferenceItem.valueOf(value);
82      }
83      return retval;
84    }
85  
86    /**
87     * Cast the provided type to this item type.
88     *
89     * @param item
90     *          the item to cast
91     * @return the original item if it is already this type, otherwise a new item
92     *         cast to this type
93     * @throws InvalidValueForCastFunctionException
94     *           if the provided {@code item} cannot be cast to this type
95     */
96    @NonNull
97    static IAnyUriItem cast(@NonNull IAnyAtomicItem item) {
98      try {
99        return item instanceof IAnyUriItem
100           ? (IAnyUriItem) item
101           : valueOf(item.asString());
102     } catch (IllegalStateException | InvalidTypeMetapathException ex) {
103       // asString can throw IllegalStateException exception
104       throw new InvalidValueForCastFunctionException(ex);
105     }
106   }
107 
108   @Override
109   default IAnyUriItem castAsType(IAnyAtomicItem item) {
110     return cast(item);
111   }
112 
113   /**
114    * Get the "wrapped" URI value.
115    *
116    * @return the underlying URI value
117    */
118   @NonNull
119   URI asUri();
120 
121   /**
122    * Determines if this URI has a scheme component, making it absolute.
123    *
124    * @return {@code true} if the URI is absolute, or {@code false} otherwise
125    */
126   default boolean isAbsolute() {
127     return asUri().isAbsolute();
128   }
129 
130   /**
131    * Determines if this URI is opaque.
132    *
133    * @return {@code true} if the URI is opaque, or {@code false} otherwise
134    * @see URI#isOpaque()
135    */
136   default boolean isOpaque() {
137     return asUri().isOpaque();
138   }
139 
140   /**
141    * Resolve the provided URI against this URI.
142    *
143    * @param other
144    *          the URI to resolve
145    * @return the resolved URI
146    * @see URI#resolve(URI)
147    */
148   @NonNull
149   default IAnyUriItem resolve(@NonNull IAnyUriItem other) {
150     return valueOf(ObjectUtils.notNull(asUri().resolve(other.asUri())));
151   }
152 
153   /**
154    * Compares this value with the argument.
155    *
156    * @param item
157    *          the item to compare with this value
158    * @return a negative integer, zero, or a positive integer if this value is less
159    *         than, equal to, or greater than the {@code item}.
160    */
161   default int compareTo(@NonNull IAnyUriItem item) {
162     return asUri().compareTo(item.asUri());
163   }
164 }