001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.jsonschema; 014 015import static org.apache.juneau.http.annotation.AnnotationUtils.*; 016import static org.apache.juneau.internal.StringUtils.*; 017 018import java.util.*; 019 020import org.apache.juneau.*; 021import org.apache.juneau.internal.*; 022import org.apache.juneau.jsonschema.annotation.*; 023import org.apache.juneau.parser.*; 024import org.apache.juneau.utils.*; 025 026/** 027 * Utilities for working with the schema annotations. 028 */ 029public class SchemaUtils { 030 031 /** 032 * Converts the specified <ja>@Schema</ja> annotation into a generic map. 033 * 034 * @param a The annotation instance. Can be <jk>null</jk>. 035 * @return The schema converted to a map, or and empty map if the annotation was null. 036 * @throws ParseException 037 */ 038 public static ObjectMap asMap(Schema a) throws ParseException { 039 if (a == null) 040 return ObjectMap.EMPTY_MAP; 041 ObjectMap om = new ObjectMap(); 042 if (empty(a)) 043 return om; 044 if (a.value().length > 0) 045 om.putAll(parseMap(a.value())); 046 return om 047 .appendSkipEmpty("additionalProperties", toObjectMap(a.additionalProperties())) 048 .appendSkipEmpty("allOf", joinnl(a.allOf())) 049 .appendSkipEmpty("collectionFormat", a.collectionFormat()) 050 .appendSkipEmpty("default", joinnl(a._default())) 051 .appendSkipEmpty("discriminator", a.discriminator()) 052 .appendSkipEmpty("description", joinnl(a.description())) 053 .appendSkipEmpty("enum", toSet(a._enum())) 054 .appendSkipEmpty("examples", parseMap(a.examples())) 055 .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum()) 056 .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum()) 057 .appendSkipEmpty("externalDocs", merge(om.getObjectMap("externalDocs"), a.externalDocs())) 058 .appendSkipEmpty("format", a.format()) 059 .appendSkipEmpty("ignore", a.ignore() ? "true" : null) 060 .appendSkipEmpty("items", merge(om.getObjectMap("items"), a.items())) 061 .appendSkipEmpty("maximum", a.maximum()) 062 .appendSkipMinusOne("maxItems", a.maxItems()) 063 .appendSkipMinusOne("maxLength", a.maxLength()) 064 .appendSkipMinusOne("maxProperties", a.maxProperties()) 065 .appendSkipEmpty("minimum", a.minimum()) 066 .appendSkipMinusOne("minItems", a.minItems()) 067 .appendSkipMinusOne("minLength", a.minLength()) 068 .appendSkipMinusOne("minProperties", a.minProperties()) 069 .appendSkipEmpty("multipleOf", a.multipleOf()) 070 .appendSkipEmpty("pattern", a.pattern()) 071 .appendSkipEmpty("properties", toObjectMap(a.properties())) 072 .appendSkipFalse("readOnly", a.readOnly()) 073 .appendSkipFalse("required", a.required()) 074 .appendSkipEmpty("title", a.title()) 075 .appendSkipEmpty("type", a.type()) 076 .appendSkipFalse("uniqueItems", a.uniqueItems()) 077 .appendSkipEmpty("xml", joinnl(a.xml())) 078 .appendSkipEmpty("x-example", joinnl(a.example())) 079 .appendSkipEmpty("$ref", a.$ref()) 080 ; 081 } 082 083 private static ObjectMap toObjectMap(String[] ss) throws ParseException { 084 if (ss.length == 0) 085 return null; 086 String s = joinnl(ss); 087 if (s.isEmpty()) 088 return null; 089 if (! isObjectMap(s, true)) 090 s = "{" + s + "}"; 091 return new ObjectMap(s); 092 } 093 094 private static ObjectMap parseMap(Object o) throws ParseException { 095 if (o == null) 096 return null; 097 if (o instanceof String[]) 098 o = joinnl((String[])o); 099 if (o instanceof String) { 100 String s = o.toString(); 101 if (s.isEmpty()) 102 return null; 103 if ("IGNORE".equalsIgnoreCase(s)) 104 return new ObjectMap().append("ignore", true); 105 if (! isObjectMap(s, true)) 106 s = "{" + s + "}"; 107 return new ObjectMap(s); 108 } 109 if (o instanceof ObjectMap) 110 return (ObjectMap)o; 111 throw new ParseException("Unexpected data type ''{0}''. Expected ObjectMap or String.", o.getClass().getName()); 112 } 113 114 private static Set<String> toSet(String[] ss) throws ParseException { 115 if (ss.length == 0) 116 return null; 117 String s = joinnl(ss); 118 if (s.isEmpty()) 119 return null; 120 Set<String> set = new ASet<>(); 121 for (Object o : StringUtils.parseListOrCdl(s)) 122 set.add(o.toString()); 123 return set; 124 } 125 126 private static ObjectMap merge(ObjectMap om, Items a) throws ParseException { 127 if (empty(a)) 128 return om; 129 if (a.value().length > 0) 130 om.putAll(parseMap(a.value())); 131 return om 132 .appendSkipEmpty("collectionFormat", a.collectionFormat()) 133 .appendSkipEmpty("default", joinnl(a._default())) 134 .appendSkipEmpty("enum", toSet(a._enum())) 135 .appendSkipEmpty("format", a.format()) 136 .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum()) 137 .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum()) 138 .appendSkipEmpty("items", merge(om.getObjectMap("items"), a.items())) 139 .appendSkipEmpty("maximum", a.maximum()) 140 .appendSkipMinusOne("maxItems", a.maxItems()) 141 .appendSkipMinusOne("maxLength", a.maxLength()) 142 .appendSkipEmpty("minimum", a.minimum()) 143 .appendSkipMinusOne("minItems", a.minItems()) 144 .appendSkipMinusOne("minLength", a.minLength()) 145 .appendSkipEmpty("multipleOf", a.multipleOf()) 146 .appendSkipEmpty("pattern", a.pattern()) 147 .appendSkipFalse("uniqueItems", a.uniqueItems()) 148 .appendSkipEmpty("type", a.type()) 149 .appendSkipEmpty("$ref", a.$ref()) 150 ; 151 } 152 153 private static ObjectMap merge(ObjectMap om, SubItems a) throws ParseException { 154 if (empty(a)) 155 return om; 156 if (a.value().length > 0) 157 om.putAll(parseMap(a.value())); 158 return om 159 .appendSkipEmpty("collectionFormat", a.collectionFormat()) 160 .appendSkipEmpty("default", joinnl(a._default())) 161 .appendSkipEmpty("enum", toSet(a._enum())) 162 .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum()) 163 .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum()) 164 .appendSkipEmpty("format", a.format()) 165 .appendSkipEmpty("items", toObjectMap(a.items())) 166 .appendSkipEmpty("maximum", a.maximum()) 167 .appendSkipMinusOne("maxItems", a.maxItems()) 168 .appendSkipMinusOne("maxLength", a.maxLength()) 169 .appendSkipEmpty("minimum", a.minimum()) 170 .appendSkipMinusOne("minItems", a.minItems()) 171 .appendSkipMinusOne("minLength", a.minLength()) 172 .appendSkipEmpty("multipleOf", a.multipleOf()) 173 .appendSkipEmpty("pattern", a.pattern()) 174 .appendSkipEmpty("type", a.type()) 175 .appendSkipFalse("uniqueItems", a.uniqueItems()) 176 .appendSkipEmpty("$ref", a.$ref()) 177 ; 178 } 179 180 private static ObjectMap merge(ObjectMap om, ExternalDocs a) throws ParseException { 181 if (empty(a)) 182 return om; 183 if (a.value().length > 0) 184 om.putAll(parseMap(a.value())); 185 return om 186 .appendSkipEmpty("description", joinnl(a.description())) 187 .appendSkipEmpty("url", a.url()) 188 ; 189 } 190}