001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.core.datatype; 028 029import com.fasterxml.jackson.core.JsonGenerator; 030import com.fasterxml.jackson.core.JsonParser; 031import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; 032 033import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; 034import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; 035import gov.nist.secauto.metaschema.core.util.ObjectUtils; 036 037import org.codehaus.stax2.XMLEventReader2; 038import org.codehaus.stax2.XMLStreamWriter2; 039import org.codehaus.stax2.evt.XMLEventFactory2; 040 041import java.io.IOException; 042import java.lang.reflect.Field; 043import java.util.List; 044import java.util.function.Supplier; 045 046import javax.xml.namespace.QName; 047import javax.xml.stream.XMLEventWriter; 048import javax.xml.stream.XMLStreamException; 049import javax.xml.stream.events.StartElement; 050import javax.xml.stream.events.XMLEvent; 051 052import edu.umd.cs.findbugs.annotations.NonNull; 053 054public interface IDataTypeAdapter<TYPE> { 055 056 /** 057 * Get the metaschema type names associated with this adapter. This name must be 058 * unique with respect to all other metaschema types. 059 * <p> 060 * At least one name must be provided, with the first name being the most 061 * preferred name. 062 * 063 * @return the name 064 */ 065 @NonNull 066 List<String> getNames(); 067 068 /** 069 * The JSON primative type of the data type. 070 * 071 * @return the JSON data type 072 */ 073 JsonFormatTypes getJsonRawType(); 074 075 /** 076 * Get the most preferred name for this data type. 077 * 078 * @return the name 079 */ 080 @NonNull 081 default String getPreferredName() { 082 return ObjectUtils.notNull(getNames().iterator().next()); 083 } 084 085 /** 086 * Get the Java class supported by this adapter. 087 * 088 * @return the Java class 089 */ 090 @NonNull 091 Class<TYPE> getJavaClass(); 092 093 /** 094 * Casts the provided value to the type associated with this adapter. 095 * 096 * @param value 097 * a value of the provided type 098 * @return the typed value 099 */ 100 @NonNull 101 TYPE toValue(@NonNull Object value); 102 103 /** 104 * Gets the value as a string suitable for writing as text. This is intended for 105 * data types that have a simple string-based structure in XML and JSON, such as 106 * for XML attributes or JSON keys. An adapter for a complex data structures 107 * that consist of XML elements will throw an 108 * {@link UnsupportedOperationException} when this is called. 109 * 110 * @param value 111 * the data to formatted as a string 112 * @return a string 113 * @throws UnsupportedOperationException 114 * if the data type cannot be represented as a string 115 */ 116 @NonNull 117 String asString(@NonNull Object value); 118 119 /** 120 * Create a copy of the provided value. 121 * 122 * @param obj 123 * the value to copy 124 * @return the copy 125 */ 126 @NonNull 127 TYPE copy(@NonNull Object obj); 128 129 /** 130 * Determines if the data type is an atomic, scalar value. Complex structures 131 * such as Markup are not considered atomic. 132 * 133 * @return {@code true} if the data type is an atomic scalar value, or 134 * {@code false} otherwise 135 */ 136 default boolean isAtomic() { 137 return true; 138 } 139 140 /** 141 * Get the java type of the associated item. 142 * 143 * @return the java associated item type 144 */ 145 @NonNull 146 Class<? extends IAnyAtomicItem> getItemClass(); 147 148 /** 149 * Construct a new item of this type using the provided value. 150 * 151 * @param value 152 * the item's value 153 * @return a new item 154 */ 155 // TODO: markup types are not atomic values. Figure out a better base type 156 // (i.e., IValuedItem) 157 @NonNull 158 IAnyAtomicItem newItem(@NonNull Object value); 159 160 /** 161 * Cast the provided item to an item of this type, if possible. 162 * 163 * @param item 164 * the atomic item to cast 165 * @return an atomic item of this type 166 * @throws InvalidValueForCastFunctionException 167 * if the provided item type cannot be cast to this item type 168 */ 169 @NonNull 170 IAnyAtomicItem cast(IAnyAtomicItem item); 171 172 /** 173 * Indicates if the adapter will parse the {@link XMLEvent#START_ELEMENT} before 174 * parsing the value data. 175 * 176 * @return {@code true} if the adapter requires the start element for parsing, 177 * or {@code false} otherwise 178 */ 179 // TODO; implement or remove this 180 boolean isParsingStartElement(); 181 182 /** 183 * Determines if adapter can parse the next element. The next element's 184 * {@link QName} is provided for this purpose. 185 * <p> 186 * This will be called when the parser encounter's an element it does not 187 * recognize. This gives the adapter a chance to request parsing of the data. 188 * 189 * @param nextElementQName 190 * the next element's namespace-qualified name 191 * @return {@code true} if the adapter will parse the element, or {@code false} 192 * otherwise 193 */ 194 // TODO: implement this 195 boolean canHandleQName(@NonNull QName nextElementQName); 196 197 /** 198 * Parses a provided string. Used to parse XML attributes, simple XML character 199 * data, and JSON/YAML property values. 200 * 201 * @param value 202 * the string value to parse 203 * @return the parsed data as the adapter's type 204 * @throws IllegalArgumentException 205 * if the data is not valid to the data type 206 */ 207 @NonNull 208 TYPE parse(@NonNull String value); 209 210 /** 211 * This method is expected to parse content starting at the next event. Parsing 212 * will continue until the next event represents content that is not handled by 213 * this adapter. This means the event stream should be positioned after any 214 * {@link XMLEvent#END_ELEMENT} that corresponds to an 215 * {@link XMLEvent#START_ELEMENT} parsed by this adapter. 216 * <p> 217 * If {@link #isParsingStartElement()} returns {@code true}, then the first 218 * event to parse will be the {@link XMLEvent#START_ELEMENT} for the element 219 * that contains the value data, then the value data. If this is the case, this 220 * method must also parse the corresponding {@link XMLEvent#END_ELEMENT}. 221 * Otherwise, the first event to parse will be the value data. 222 * <p> 223 * The value data is expected to be parsed completely, leaving the event stream 224 * on a peeked event corresponding to content that is not handled by this 225 * method. 226 * 227 * @param eventReader 228 * the XML parser used to read the parsed value 229 * @return the parsed value 230 * @throws IOException 231 * if a parsing error occurs 232 */ 233 @NonNull 234 TYPE parse(@NonNull XMLEventReader2 eventReader) throws IOException; 235 236 /** 237 * Parses a JSON property value. 238 * 239 * @param parser 240 * the JSON parser used to read the parsed value 241 * @return the parsed value 242 * @throws IOException 243 * if a parsing error occurs 244 */ 245 @NonNull 246 TYPE parse(@NonNull JsonParser parser) throws IOException; 247 248 /** 249 * Parses a provided string using {@link #parse(String)}. 250 * <p> 251 * This method may pre-parse the data and then return copies, since the data can 252 * only be parsed once, but the supplier might be called multiple times. 253 * 254 * @param value 255 * the string value to parse 256 * @return a supplier that will provide new instances of the parsed data 257 * @throws IOException 258 * if an error occurs while parsing 259 * @see #parse(String) 260 */ 261 @NonNull 262 default Supplier<TYPE> parseAndSupply(@NonNull String value) throws IOException { 263 TYPE retval = parse(value); 264 return () -> copy(retval); 265 } 266 267 /** 268 * Parses a provided string using 269 * {@link IDataTypeAdapter#parse(XMLEventReader2)}. 270 * <p> 271 * This method may pre-parse the data and then return copies, since the data can 272 * only be parsed once, but the supplier might be called multiple times. 273 * 274 * @param eventReader 275 * the XML parser used to read the parsed value 276 * @return a supplier that will provide new instances of the parsed data 277 * @throws IOException 278 * if an error occurs while parsing 279 * @see #parse(String) 280 * @see #parse(XMLEventReader2) 281 */ 282 @NonNull 283 default Supplier<TYPE> parseAndSupply(@NonNull XMLEventReader2 eventReader) throws IOException { 284 TYPE retval = parse(eventReader); 285 return () -> copy(retval); 286 } 287 288 /** 289 * Parses a provided string using {@link #parse(JsonParser)}. 290 * <p> 291 * This method may pre-parse the data and then return copies, since the data can 292 * only be parsed once, but the supplier might be called multiple times. 293 * 294 * @param parser 295 * the JSON parser used to read the parsed value 296 * @return a supplier that will provide new instances of the parsed data 297 * @throws IOException 298 * if an error occurs while parsing 299 * @see #parse(String) 300 * @see #parse(JsonParser) 301 */ 302 @NonNull 303 default Supplier<TYPE> parseAndSupply(@NonNull JsonParser parser) throws IOException { 304 TYPE retval = parse(parser); 305 return () -> copy(retval); 306 } 307 308 /** 309 * Writes the provided Java class instance data as XML. The parent element 310 * information is provided as a {@link StartElement} event, which allows 311 * namespace information to be obtained from the parent element using the 312 * {@link StartElement#getName()} and {@link StartElement#getNamespaceContext()} 313 * methods, which can be used when writing the provided instance value. 314 * 315 * @param instance 316 * the {@link Field} instance value to write 317 * @param parent 318 * the {@link StartElement} XML event that is the parent of the data to 319 * write 320 * @param eventFactory 321 * the XML event factory used to generate XML writing events 322 * @param eventWriter 323 * the XML writer used to output XML as events 324 * @throws XMLStreamException 325 * if an unexpected error occurred while processing the XML output 326 * @throws IOException 327 * if an unexpected error occurred while writing to the output stream 328 */ 329 void writeXmlValue(@NonNull Object instance, @NonNull StartElement parent, @NonNull XMLEventFactory2 eventFactory, 330 @NonNull XMLEventWriter eventWriter) 331 throws IOException, XMLStreamException; 332 333 /** 334 * Writes the provided Java class instance data as XML. The parent element 335 * information is provided as an XML {@link QName}, which allows namespace 336 * information to be obtained from the parent element. Additional namespace 337 * information can be gathered using the 338 * {@link XMLStreamWriter2#getNamespaceContext()} method, which can be used when 339 * writing the provided instance value. 340 * 341 * @param instance 342 * the {@link Field} instance value to write 343 * @param parentName 344 * the qualified name of the XML data's parent element 345 * @param writer 346 * the XML writer used to output the XML data 347 * @throws XMLStreamException 348 * if an unexpected error occurred while processing the XML output 349 */ 350 void writeXmlValue(@NonNull Object instance, @NonNull QName parentName, @NonNull XMLStreamWriter2 writer) 351 throws XMLStreamException; 352 353 /** 354 * Writes the provided Java class instance as a JSON/YAML field value. 355 * 356 * @param instance 357 * the {@link Field} instance value to write 358 * @param writer 359 * the JSON/YAML writer used to output the JSON/YAML data 360 * @throws IOException 361 * if an unexpected error occurred while writing the JSON/YAML output 362 */ 363 void writeJsonValue(@NonNull Object instance, @NonNull JsonGenerator writer) throws IOException; 364 365 /** 366 * Gets the default value to use as the JSON/YAML field name for a Metaschema 367 * field value if no JSON value key flag or name is configured. 368 * 369 * @return the default field name to use 370 */ 371 @NonNull 372 String getDefaultJsonValueKey(); 373 374 /** 375 * Determines if the data type's value is allowed to be unwrapped in XML when 376 * the value is a field value. 377 * 378 * @return {@code true} if allowed, or {@code false} otherwise. 379 */ 380 boolean isUnrappedValueAllowedInXml(); 381 382 /** 383 * Determines if the datatype uses mixed text and element content in XML. 384 * 385 * @return {@code true} if the datatype uses mixed text and element content in 386 * XML, or {@code false} otherwise 387 */ 388 boolean isXmlMixed(); 389}