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.collections.*; 021import org.apache.juneau.internal.*; 022import org.apache.juneau.jsonschema.annotation.*; 023import org.apache.juneau.parser.*; 024 025/** 026 * Utilities for working with the schema annotations. 027 */ 028public class SchemaUtils { 029 030 /** 031 * Converts the specified <ja>@Schema</ja> annotation into a generic map. 032 * 033 * @param a The annotation instance. Can be <jk>null</jk>. 034 * @return The schema converted to a map, or and empty map if the annotation was null. 035 * @throws ParseException Malformed input encountered. 036 */ 037 public static OMap asMap(Schema a) throws ParseException { 038 if (a == null) 039 return OMap.EMPTY_MAP; 040 OMap om = new OMap(); 041 if (empty(a)) 042 return om; 043 if (a.value().length > 0) 044 om.putAll(parseMap(a.value())); 045 return om 046 .ase("additionalProperties", toOMap(a.additionalProperties())) 047 .ase("allOf", joinnl(a.allOf())) 048 .ase("collectionFormat", a.collectionFormat(), a.cf()) 049 .ase("default", joinnl(a._default(), a.df())) 050 .ase("discriminator", a.discriminator()) 051 .ase("description", joinnl(a.description(), a.d())) 052 .ase("enum", toSet(a._enum()), toSet(a.e())) 053 .ase("examples", parseMap(a.examples()), parseMap(a.exs())) 054 .asf("exclusiveMaximum", a.exclusiveMaximum() || a.emax()) 055 .asf("exclusiveMinimum", a.exclusiveMinimum() || a.emin()) 056 .ase("externalDocs", merge(om.getMap("externalDocs"), a.externalDocs())) 057 .ase("format", a.format(), a.f()) 058 .ase("ignore", a.ignore() ? "true" : null) 059 .ase("items", merge(om.getMap("items"), a.items())) 060 .ase("maximum", a.maximum(), a.max()) 061 .asmo("maxItems", a.maxItems(), a.maxi()) 062 .asmo("maxLength", a.maxLength(), a.maxl()) 063 .asmo("maxProperties", a.maxProperties(), a.maxp()) 064 .ase("minimum", a.minimum(), a.min()) 065 .asmo("minItems", a.minItems(), a.mini()) 066 .asmo("minLength", a.minLength(), a.minl()) 067 .asmo("minProperties", a.minProperties(), a.minp()) 068 .ase("multipleOf", a.multipleOf(), a.mo()) 069 .ase("pattern", a.pattern(), a.p()) 070 .ase("properties", toOMap(a.properties())) 071 .asf("readOnly", a.readOnly() || a.ro()) 072 .asf("required", a.required() || a.r()) 073 .ase("title", a.title()) 074 .ase("type", a.type(), a.t()) 075 .asf("uniqueItems", a.uniqueItems() || a.ui()) 076 .ase("xml", joinnl(a.xml())) 077 .ase("x-example", joinnl(a.example(), a.ex())) 078 .ase("$ref", a.$ref()) 079 ; 080 } 081 082 private static OMap toOMap(String[] ss) throws ParseException { 083 if (ss.length == 0) 084 return null; 085 String s = joinnl(ss); 086 if (s.isEmpty()) 087 return null; 088 if (! isJsonObject(s, true)) 089 s = "{" + s + "}"; 090 return OMap.ofJson(s); 091 } 092 093 private static OMap parseMap(Object o) throws ParseException { 094 if (o == null) 095 return null; 096 if (o instanceof String[]) 097 o = joinnl((String[])o); 098 if (o instanceof String) { 099 String s = o.toString(); 100 if (s.isEmpty()) 101 return null; 102 if ("IGNORE".equalsIgnoreCase(s)) 103 return OMap.of("ignore", true); 104 if (! isJsonObject(s, true)) 105 s = "{" + s + "}"; 106 return OMap.ofJson(s); 107 } 108 if (o instanceof OMap) 109 return (OMap)o; 110 throw new ParseException("Unexpected data type ''{0}''. Expected OMap or String.", o.getClass().getName()); 111 } 112 113 private static Set<String> toSet(String[] ss) throws ParseException { 114 if (ss.length == 0) 115 return null; 116 String s = joinnl(ss); 117 if (s.isEmpty()) 118 return null; 119 Set<String> set = ASet.of(); 120 for (Object o : StringUtils.parseListOrCdl(s)) 121 set.add(o.toString()); 122 return set; 123 } 124 125 private static OMap merge(OMap om, Items a) throws ParseException { 126 if (empty(a)) 127 return om; 128 if (a.value().length > 0) 129 om.putAll(parseMap(a.value())); 130 return om 131 .ase("collectionFormat", a.collectionFormat(), a.cf()) 132 .ase("default", joinnl(a._default(), a.df())) 133 .ase("enum", toSet(a._enum()), toSet(a.e())) 134 .ase("format", a.format(), a.f()) 135 .asf("exclusiveMaximum", a.exclusiveMaximum() || a.emax()) 136 .asf("exclusiveMinimum", a.exclusiveMinimum() || a.emin()) 137 .ase("items", merge(om.getMap("items"), a.items())) 138 .ase("maximum", a.maximum(), a.max()) 139 .asmo("maxItems", a.maxItems(), a.maxi()) 140 .asmo("maxLength", a.maxLength(), a.maxl()) 141 .ase("minimum", a.minimum(), a.min()) 142 .asmo("minItems", a.minItems(), a.mini()) 143 .asmo("minLength", a.minLength(), a.minl()) 144 .ase("multipleOf", a.multipleOf(), a.mo()) 145 .ase("pattern", a.pattern(), a.p()) 146 .asf("uniqueItems", a.uniqueItems() || a.ui()) 147 .ase("type", a.type(), a.t()) 148 .ase("$ref", a.$ref()) 149 ; 150 } 151 152 private static OMap merge(OMap om, SubItems a) throws ParseException { 153 if (empty(a)) 154 return om; 155 if (a.value().length > 0) 156 om.putAll(parseMap(a.value())); 157 return om 158 .ase("collectionFormat", a.collectionFormat(), a.cf()) 159 .ase("default", joinnl(a._default(), a.df())) 160 .ase("enum", toSet(a._enum()), toSet(a.e())) 161 .asf("exclusiveMaximum", a.exclusiveMaximum() || a.emax()) 162 .asf("exclusiveMinimum", a.exclusiveMinimum() || a.emin()) 163 .ase("format", a.format(), a.f()) 164 .ase("items", toOMap(a.items())) 165 .ase("maximum", a.maximum(), a.max()) 166 .asmo("maxItems", a.maxItems(), a.maxi()) 167 .asmo("maxLength", a.maxLength(), a.maxl()) 168 .ase("minimum", a.minimum(), a.min()) 169 .asmo("minItems", a.minItems(), a.mini()) 170 .asmo("minLength", a.minLength(), a.minl()) 171 .ase("multipleOf", a.multipleOf(), a.mo()) 172 .ase("pattern", a.pattern(), a.p()) 173 .ase("type", a.type(), a.t()) 174 .asf("uniqueItems", a.uniqueItems() || a.ui()) 175 .ase("$ref", a.$ref()) 176 ; 177 } 178 179 private static OMap merge(OMap om, ExternalDocs a) throws ParseException { 180 if (empty(a)) 181 return om; 182 if (a.value().length > 0) 183 om.putAll(parseMap(a.value())); 184 return om 185 .ase("description", joinnl(a.description())) 186 .ase("url", a.url()) 187 ; 188 } 189 190 private static String joinnl(String[]...s) { 191 for (String[] ss : s) { 192 if (ss.length != 0) 193 return StringUtils.joinnl(ss).trim(); 194 } 195 return ""; 196 } 197}