1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.core.model;
7   
8   import gov.nist.secauto.metaschema.core.MetaschemaConstants;
9   import gov.nist.secauto.metaschema.core.util.CollectionUtil;
10  
11  import java.util.Map;
12  import java.util.Objects;
13  import java.util.Set;
14  
15  import edu.umd.cs.findbugs.annotations.NonNull;
16  
17  /**
18   * A marker interface for implementations that supports the mapping of a
19   * namespace valued key to a set of values.
20   */
21  @FunctionalInterface
22  public interface IAttributable {
23    /**
24     * The default key namespace for a property.
25     */
26    @NonNull
27    String DEFAULT_PROPERY_NAMESPACE = MetaschemaConstants.METASCHEMA_NAMESPACE;
28  
29    /**
30     * Get the mapping of property key to values.
31     *
32     * @return the mapping
33     */
34    @NonNull
35    Map<Key, Set<String>> getProperties();
36  
37    /**
38     * Determine if a property is defined.
39     *
40     * @param key
41     *          the key of the property
42     * @return {@code true} if the property is defined or {@code false} otherwise
43     */
44    default boolean hasProperty(@NonNull Key key) {
45      return getProperties().containsKey(key);
46    }
47  
48    /**
49     * Get the values associated with a given property key.
50     *
51     * @param key
52     *          the key of the property
53     * @return the values or an empty set
54     */
55    @NonNull
56    default Set<String> getPropertyValues(@NonNull Key key) {
57      Set<String> retval = getProperties().get(key);
58      if (retval == null) {
59        retval = CollectionUtil.emptySet();
60      }
61      return retval;
62    }
63  
64    /**
65     * Determine if a given property, with a given {@code key}, has the identified
66     * {@code value}.
67     *
68     * @param key
69     *          the key of the property
70     * @param value
71     *          the expected property value
72     * @return {@code true} if the property value is defined or {@code false}
73     *         otherwise
74     */
75    default boolean hasPropertyValue(@NonNull Key key, @NonNull String value) {
76      Set<String> values = getProperties().get(key);
77      return values != null && values.contains(value);
78    }
79  
80    /**
81     * Get a property key based on the provided name and namespace.
82     *
83     * @param name
84     *          the name portion of a property key
85     * @param namespace
86     *          the namespace portion of a property key
87     * @return the property key
88     */
89    @NonNull
90    static Key key(@NonNull String name, @NonNull String namespace) {
91      return new Key(name, namespace);
92    }
93  
94    /**
95     * Get a property key based on the provided name and the default namespace.
96     *
97     * @param name
98     *          the name portion of a property key
99     * @return the property key
100    * @see #DEFAULT_PROPERY_NAMESPACE
101    */
102   @NonNull
103   static Key key(@NonNull String name) {
104     return new Key(name);
105   }
106 
107   /**
108    * Represents a property key based on a name and a namespace.
109    */
110   // FIXME: use IEnhancedQName instead of name and namespace, or replace key
111   // altogether with an integer value
112   final class Key {
113     @NonNull
114     private final String name;
115     @NonNull
116     private final String namespace;
117 
118     private Key(@NonNull String name) {
119       this(name, DEFAULT_PROPERY_NAMESPACE);
120     }
121 
122     private Key(@NonNull String name, @NonNull String namespace) {
123       this.name = name;
124       this.namespace = namespace;
125     }
126 
127     /**
128      * Get the property key's name portion.
129      *
130      * @return the name
131      */
132     @NonNull
133     public String getName() {
134       return name;
135     }
136 
137     /**
138      * Get the property key's namespace portion.
139      *
140      * @return the name
141      */
142     @NonNull
143     public String getNamespace() {
144       return namespace;
145     }
146 
147     @Override
148     public int hashCode() {
149       return Objects.hash(name, namespace);
150     }
151 
152     @Override
153     public boolean equals(Object obj) {
154       if (this == obj) {
155         return true;
156       }
157       if (!(obj instanceof Key)) {
158         return false;
159       }
160       Key other = (Key) obj;
161       return Objects.equals(name, other.name) && Objects.equals(namespace, other.namespace);
162     }
163   }
164 }