001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package dev.metaschema.core.model; 007 008import java.util.Map; 009import java.util.Objects; 010import java.util.Set; 011 012import dev.metaschema.core.MetaschemaConstants; 013import dev.metaschema.core.util.CollectionUtil; 014import edu.umd.cs.findbugs.annotations.NonNull; 015 016/** 017 * A marker interface for implementations that supports the mapping of a 018 * namespace valued key to a set of values. 019 */ 020@FunctionalInterface 021public interface IAttributable { 022 /** 023 * The default key namespace for a property. 024 */ 025 @NonNull 026 String DEFAULT_PROPERY_NAMESPACE = MetaschemaConstants.METASCHEMA_NAMESPACE; 027 028 /** 029 * Get the mapping of property key to values. 030 * 031 * @return the mapping 032 */ 033 @NonNull 034 Map<Key, Set<String>> getProperties(); 035 036 /** 037 * Determine if a property is defined. 038 * 039 * @param key 040 * the key of the property 041 * @return {@code true} if the property is defined or {@code false} otherwise 042 */ 043 default boolean hasProperty(@NonNull Key key) { 044 return getProperties().containsKey(key); 045 } 046 047 /** 048 * Get the values associated with a given property key. 049 * 050 * @param key 051 * the key of the property 052 * @return the values or an empty set 053 */ 054 @NonNull 055 default Set<String> getPropertyValues(@NonNull Key key) { 056 Set<String> retval = getProperties().get(key); 057 if (retval == null) { 058 retval = CollectionUtil.emptySet(); 059 } 060 return retval; 061 } 062 063 /** 064 * Determine if a given property, with a given {@code key}, has the identified 065 * {@code value}. 066 * 067 * @param key 068 * the key of the property 069 * @param value 070 * the expected property value 071 * @return {@code true} if the property value is defined or {@code false} 072 * otherwise 073 */ 074 default boolean hasPropertyValue(@NonNull Key key, @NonNull String value) { 075 Set<String> values = getProperties().get(key); 076 return values != null && values.contains(value); 077 } 078 079 /** 080 * Get a property key based on the provided name and namespace. 081 * 082 * @param name 083 * the name portion of a property key 084 * @param namespace 085 * the namespace portion of a property key 086 * @return the property key 087 */ 088 @NonNull 089 static Key key(@NonNull String name, @NonNull String namespace) { 090 return new Key(name, namespace); 091 } 092 093 /** 094 * Get a property key based on the provided name and the default namespace. 095 * 096 * @param name 097 * the name portion of a property key 098 * @return the property key 099 * @see #DEFAULT_PROPERY_NAMESPACE 100 */ 101 @NonNull 102 static Key key(@NonNull String name) { 103 return new Key(name); 104 } 105 106 /** 107 * Represents a property key based on a name and a namespace. 108 */ 109 // FIXME: use IEnhancedQName instead of name and namespace, or replace key 110 // altogether with an integer value 111 final class Key { 112 @NonNull 113 private final String name; 114 @NonNull 115 private final String namespace; 116 117 private Key(@NonNull String name) { 118 this(name, DEFAULT_PROPERY_NAMESPACE); 119 } 120 121 private Key(@NonNull String name, @NonNull String namespace) { 122 this.name = name; 123 this.namespace = namespace; 124 } 125 126 /** 127 * Get the property key's name portion. 128 * 129 * @return the name 130 */ 131 @NonNull 132 public String getName() { 133 return name; 134 } 135 136 /** 137 * Get the property key's namespace portion. 138 * 139 * @return the name 140 */ 141 @NonNull 142 public String getNamespace() { 143 return namespace; 144 } 145 146 @Override 147 public int hashCode() { 148 return Objects.hash(name, namespace); 149 } 150 151 @Override 152 public boolean equals(Object obj) { 153 if (this == obj) { 154 return true; 155 } 156 if (!(obj instanceof Key)) { 157 return false; 158 } 159 Key other = (Key) obj; 160 return Objects.equals(name, other.name) && Objects.equals(namespace, other.namespace); 161 } 162 } 163}