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.datatype.adapter.MetaschemaDataTypeProvider;
9   import dev.metaschema.core.metapath.function.InvalidValueForCastFunctionException;
10  import dev.metaschema.core.metapath.item.atomic.impl.BooleanItemImpl;
11  import dev.metaschema.core.metapath.type.IAtomicOrUnionType;
12  import dev.metaschema.core.metapath.type.InvalidTypeMetapathException;
13  import edu.umd.cs.findbugs.annotations.NonNull;
14  
15  /**
16   * An atomic Metapath item with a boolean value.
17   */
18  public interface IBooleanItem extends IAnyAtomicItem {
19    /**
20     * The boolean item value of {@code true}.
21     */
22    @NonNull
23    IBooleanItem TRUE = new BooleanItemImpl(true);
24    /**
25     * The boolean item value of {@code false}.
26     */
27    @NonNull
28    IBooleanItem FALSE = new BooleanItemImpl(false);
29  
30    /**
31     * Get the type information for this item.
32     *
33     * @return the type information
34     */
35    @NonNull
36    static IAtomicOrUnionType<IBooleanItem> type() {
37      return MetaschemaDataTypeProvider.BOOLEAN.getItemType();
38    }
39  
40    @Override
41    default IAtomicOrUnionType<IBooleanItem> getType() {
42      return type();
43    }
44  
45    /**
46     * Construct a new boolean item using the provided string {@code value}.
47     * <p>
48     * The item will be {@link #TRUE} if the value is "1" or "true", or
49     * {@link #FALSE} otherwise
50     *
51     * @param value
52     *          a string representing a boolean value
53     * @return the new item
54     * @throws InvalidTypeMetapathException
55     *           if the provided value is not a valid boolean value
56     */
57    @NonNull
58    static IBooleanItem valueOf(@NonNull String value) {
59      IBooleanItem retval;
60      if ("1".equals(value)) {
61        retval = TRUE;
62      } else {
63        try {
64          retval = valueOf(MetaschemaDataTypeProvider.BOOLEAN.parse(value));
65        } catch (IllegalArgumentException ex) {
66          throw new InvalidTypeMetapathException(
67              null,
68              String.format("Invalid boolean value '%s'. %s",
69                  value,
70                  ex.getLocalizedMessage()),
71              ex);
72        }
73      }
74      return retval;
75    }
76  
77    /**
78     * Construct a new boolean item using the provided {@code value}.
79     *
80     * @param value
81     *          a boolean
82     * @return the new item
83     */
84    @NonNull
85    static IBooleanItem valueOf(boolean value) {
86      return value ? TRUE : FALSE;
87    }
88  
89    /**
90     * Cast the provided type to this item type.
91     *
92     * @param item
93     *          the item to cast
94     * @return the original item if it is already this type, otherwise a new item
95     *         cast to this type
96     * @throws InvalidValueForCastFunctionException
97     *           if the provided {@code item} cannot be cast to this type
98     */
99    @NonNull
100   static IBooleanItem cast(@NonNull IAnyAtomicItem item) {
101     IBooleanItem retval;
102     if (item instanceof INumericItem) {
103       retval = valueOf(((INumericItem) item).toEffectiveBoolean());
104     } else {
105       try {
106         retval = valueOf(INumericItem.cast(item).toEffectiveBoolean());
107       } catch (InvalidValueForCastFunctionException ex) {
108         try {
109           retval = valueOf(item.asString());
110         } catch (IllegalStateException | InvalidTypeMetapathException ex2) {
111           // asString can throw IllegalStateException exception
112           InvalidValueForCastFunctionException thrown = new InvalidValueForCastFunctionException(ex2);
113           thrown.addSuppressed(ex);
114           throw thrown;
115         }
116       }
117     }
118     return retval;
119   }
120 
121   @Override
122   default IBooleanItem castAsType(IAnyAtomicItem item) {
123     return cast(item);
124   }
125 
126   /**
127    * Get the "wrapped" boolean value.
128    *
129    * @return the underlying boolean value
130    */
131   boolean toBoolean();
132 
133   /**
134    * Get the boolean negation of this value.
135    *
136    * @return the negated boolean value
137    */
138   @NonNull
139   default IBooleanItem negate() {
140     return this.toBoolean() ? FALSE : TRUE;
141   }
142 
143   /**
144    * Compares this value with the argument.
145    *
146    * @param item
147    *          the item to compare with this value
148    * @return a negative integer, zero, or a positive integer if this value is less
149    *         than, equal to, or greater than the {@code item}.
150    */
151   default int compareTo(@NonNull IBooleanItem item) {
152     return Boolean.compare(toBoolean(), item.toBoolean());
153   }
154 }