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    @SuppressWarnings("PMD.ShortMethodName")
27    @NonNull
28    static IArgument of(@NonNull IEnhancedQName name, @NonNull ISequenceType sequenceType) {
29      return new ArgumentImpl(name, sequenceType);
30    }
31  
32    /**
33     * Get the argument's name.
34     *
35     * @return the argument's name
36     */
37    @NonNull
38    IEnhancedQName getName();
39  
40    /**
41     * Get information about the type of sequence supported by the argument.
42     *
43     * @return the sequence information
44     */
45    @NonNull
46    ISequenceType getSequenceType();
47  
48    /**
49     * Get the signature of the argument.
50     *
51     * @return the argument's signature
52     */
53    @NonNull
54    String toSignature();
55  
56    /**
57     * Get a new argument builder.
58     *
59     * @return the new argument builder
60     */
61    @NonNull
62    static Builder builder() {
63      return new Builder();
64    }
65  
66    @NonNull
67    static String resolveArgumentName(@NonNull String prefix) {
68      if (!"".equals(prefix)) {
69        throw new UnsupportedOperationException("Lexical qualified names are not allowed.");
70      }
71      return "";
72    }
73  
74    /**
75     * Used to create an argument's signature using a builder pattern.
76     */
77    final class Builder {
78      private IEnhancedQName name;
79      @NonNull
80      private IItemType type;
81      private Occurrence occurrence;
82  
83      private Builder() {
84        // construct a new non-initialized builder
85        this.type = IItem.type();
86      }
87  
88      /**
89       * Define the name of the function argument.
90       *
91       * @param name
92       *          the argument's name
93       * @return this builder
94       */
95      @NonNull
96      public Builder name(@NonNull String name) {
97        if (Objects.requireNonNull(name, "name").isBlank()) {
98          throw new IllegalArgumentException("the name must be non-blank");
99        }
100       this.name = EQNameFactory.instance().parseName(name, IArgument::resolveArgumentName);
101       return this;
102     }
103 
104     /**
105      * Define the type of the function argument.
106      * <p>
107      * By default an argument has the type {@link IItem}.
108      *
109      * @param name
110      *          the qualified name of the argument's type
111      * @return this builder
112      */
113     @NonNull
114     public Builder type(@NonNull IEnhancedQName name) {
115       try {
116         this.type = StaticContext.lookupAtomicType(name);
117       } catch (StaticMetapathException ex) {
118         throw new IllegalArgumentException(
119             String.format("No data type with the name '%s'.", name), ex);
120       }
121       return this;
122     }
123 
124     /**
125      * Define the type of the function argument.
126      * <p>
127      * By default an argument has the type {@link IItem}.
128      *
129      * @param type
130      *          the argument's type
131      * @return this builder
132      */
133     @NonNull
134     public Builder type(@NonNull IItemType type) {
135       this.type = type;
136       return this;
137     }
138 
139     /**
140      * Identifies the argument's cardinality as a single, optional item (zero or
141      * one).
142      *
143      * @return this builder
144      */
145     @NonNull
146     public Builder zeroOrOne() {
147       return occurrence(Occurrence.ZERO_OR_ONE);
148     }
149 
150     /**
151      * Identifies the argument's cardinality as a single, required item (one).
152      *
153      * @return this builder
154      */
155     @NonNull
156     public Builder one() {
157       return occurrence(Occurrence.ONE);
158     }
159 
160     /**
161      * Identifies the argument's cardinality as an optional series of items (zero or
162      * more).
163      *
164      * @return this builder
165      */
166     @NonNull
167     public Builder zeroOrMore() {
168       return occurrence(Occurrence.ZERO_OR_MORE);
169     }
170 
171     /**
172      * Identifies the argument's cardinality as a required series of items (one or
173      * more).
174      *
175      * @return this builder
176      */
177     @NonNull
178     public Builder oneOrMore() {
179       return occurrence(Occurrence.ONE_OR_MORE);
180     }
181 
182     @NonNull
183     private Builder occurrence(@NonNull Occurrence occurrence) {
184       Objects.requireNonNull(occurrence, "occurrence");
185       this.occurrence = occurrence;
186       return this;
187     }
188 
189     /**
190      * Builds the argument's signature.
191      *
192      * @return the argument's signature
193      */
194     @NonNull
195     public IArgument build() {
196       return new ArgumentImpl(
197           ObjectUtils.requireNonNull(name, "the argument name must not be null"),
198           ISequenceType.of(type, ObjectUtils.requireNonNull(occurrence, "occurrence")));
199     }
200   }
201 }