1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.core.metapath.function;
7   
8   import gov.nist.secauto.metaschema.core.metapath.StaticContext;
9   import gov.nist.secauto.metaschema.core.metapath.StaticMetapathException;
10  import gov.nist.secauto.metaschema.core.metapath.item.IItem;
11  import gov.nist.secauto.metaschema.core.metapath.type.IItemType;
12  import gov.nist.secauto.metaschema.core.metapath.type.ISequenceType;
13  import gov.nist.secauto.metaschema.core.metapath.type.Occurrence;
14  import gov.nist.secauto.metaschema.core.qname.EQNameFactory;
15  import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
16  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
17  
18  import java.util.Objects;
19  
20  import edu.umd.cs.findbugs.annotations.NonNull;
21  
22  /**
23   * Represents a single function argument signature.
24   */
25  public interface IArgument {
26    /**
27     * Create a new argument with the provided name and sequence type.
28     *
29     * @param name
30     *          the argument's name
31     * @param sequenceType
32     *          the argument's sequence type
33     * @return a new argument instance
34     */
35    @NonNull
36    static IArgument of(@NonNull IEnhancedQName name, @NonNull ISequenceType sequenceType) {
37      return new ArgumentImpl(name, sequenceType);
38    }
39  
40    /**
41     * Get the argument's name.
42     *
43     * @return the argument's name
44     */
45    @NonNull
46    IEnhancedQName getName();
47  
48    /**
49     * Get information about the type of sequence supported by the argument.
50     *
51     * @return the sequence information
52     */
53    @NonNull
54    ISequenceType getSequenceType();
55  
56    /**
57     * Get the signature of the argument.
58     *
59     * @return the argument's signature
60     */
61    @NonNull
62    String toSignature();
63  
64    /**
65     * Get a new argument builder.
66     *
67     * @return the new argument builder
68     */
69    @NonNull
70    static Builder builder() {
71      return new Builder();
72    }
73  
74    /**
75     * Resolve an argument name from a prefix.
76     *
77     * @param prefix
78     *          the prefix to resolve
79     * @return the resolved argument name
80     * @throws UnsupportedOperationException
81     *           if a non-empty prefix is provided
82     */
83    @NonNull
84    static String resolveArgumentName(@NonNull String prefix) {
85      if (!"".equals(prefix)) {
86        throw new UnsupportedOperationException("Lexical qualified names are not allowed.");
87      }
88      return "";
89    }
90  
91    /**
92     * Used to create an argument's signature using a builder pattern.
93     */
94    final class Builder {
95      private IEnhancedQName name;
96      @NonNull
97      private IItemType type;
98      private Occurrence occurrence;
99  
100     private Builder() {
101       // construct a new non-initialized builder
102       this.type = IItem.type();
103     }
104 
105     /**
106      * Define the name of the function argument.
107      *
108      * @param name
109      *          the argument's name
110      * @return this builder
111      */
112     @NonNull
113     public Builder name(@NonNull String name) {
114       if (Objects.requireNonNull(name, "name").isBlank()) {
115         throw new IllegalArgumentException("the name must be non-blank");
116       }
117       this.name = EQNameFactory.instance().parseName(name, IArgument::resolveArgumentName);
118       return this;
119     }
120 
121     /**
122      * Define the type of the function argument.
123      * <p>
124      * By default an argument has the type {@link IItem}.
125      *
126      * @param name
127      *          the qualified name of the argument's type
128      * @return this builder
129      */
130     @NonNull
131     public Builder type(@NonNull IEnhancedQName name) {
132       try {
133         this.type = StaticContext.lookupAtomicType(name);
134       } catch (StaticMetapathException ex) {
135         throw new IllegalArgumentException(
136             String.format("No data type with the name '%s'.", name), ex);
137       }
138       return this;
139     }
140 
141     /**
142      * Define the type of the function argument.
143      * <p>
144      * By default an argument has the type {@link IItem}.
145      *
146      * @param type
147      *          the argument's type
148      * @return this builder
149      */
150     @NonNull
151     public Builder type(@NonNull IItemType type) {
152       this.type = type;
153       return this;
154     }
155 
156     /**
157      * Identifies the argument's cardinality as a single, optional item (zero or
158      * one).
159      *
160      * @return this builder
161      */
162     @NonNull
163     public Builder zeroOrOne() {
164       return occurrence(Occurrence.ZERO_OR_ONE);
165     }
166 
167     /**
168      * Identifies the argument's cardinality as a single, required item (one).
169      *
170      * @return this builder
171      */
172     @NonNull
173     public Builder one() {
174       return occurrence(Occurrence.ONE);
175     }
176 
177     /**
178      * Identifies the argument's cardinality as an optional series of items (zero or
179      * more).
180      *
181      * @return this builder
182      */
183     @NonNull
184     public Builder zeroOrMore() {
185       return occurrence(Occurrence.ZERO_OR_MORE);
186     }
187 
188     /**
189      * Identifies the argument's cardinality as a required series of items (one or
190      * more).
191      *
192      * @return this builder
193      */
194     @NonNull
195     public Builder oneOrMore() {
196       return occurrence(Occurrence.ONE_OR_MORE);
197     }
198 
199     @NonNull
200     private Builder occurrence(@NonNull Occurrence occurrence) {
201       Objects.requireNonNull(occurrence, "occurrence");
202       this.occurrence = occurrence;
203       return this;
204     }
205 
206     /**
207      * Builds the argument's signature.
208      *
209      * @return the argument's signature
210      */
211     @NonNull
212     public IArgument build() {
213       return new ArgumentImpl(
214           ObjectUtils.requireNonNull(name, "the argument name must not be null"),
215           ISequenceType.of(type, ObjectUtils.requireNonNull(occurrence, "occurrence")));
216     }
217   }
218 }