001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. */ 016package org.apache.juneau.marshall.jena; 017 018 019import org.apache.juneau.commons.bean.*; 020import org.apache.juneau.commons.collections.*; 021import org.apache.juneau.marshall.*; 022import org.apache.juneau.marshall.serializer.*; 023import org.apache.juneau.marshall.xml.*; 024import static org.apache.juneau.commons.utils.Shorts.*; 025 026/** 027 * Stream-based RDF serializer for binary formats (RDF/THRIFT, RDF/PROTO). 028 * 029 * <p> 030 * Extends {@link OutputStreamSerializer} and delegates RDF metadata to an internal {@link RdfSerializer}. 031 * 032 * <h5 class='section'>Notes:</h5><ul> 033 * <li class='note'>This class is thread safe and reusable. 034 * </ul> 035 * 036 * <h5 class='section'>See Also:</h5><ul> 037 * <li class='link'>{doc jmr.RdfDetails} 038 * </ul> 039 */ 040@SuppressWarnings({ 041 "java:S110", // Deep inheritance inherent to the RDF serializer hierarchy. 042 "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants 043}) 044public class RdfStreamSerializer extends OutputStreamSerializer implements RdfMetaProvider { 045 046 /** 047 * Builder class. 048 */ 049 @SuppressWarnings({ 050 "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name. 051 }) 052 public abstract static class Builder<SELF extends Builder<SELF>> extends OutputStreamSerializer.Builder<SELF> { 053 054 private static final Cache<HashKey,RdfStreamSerializer> CACHE = Cache.of(HashKey.class, RdfStreamSerializer.class).build(); 055 056 private String language; 057 private boolean addBeanTypes; 058 private RdfSerializer rdfSerializer; 059 060 /** 061 * Constructor, default settings. 062 */ 063 protected Builder() { 064 language = Constants.LANG_RDFTHRIFT; 065 } 066 067 /** 068 * Copy constructor. 069 * 070 * @param copyFrom The builder to copy from. 071 * <br>Cannot be <jk>null</jk>. 072 */ 073 protected Builder(Builder<?> copyFrom) { 074 super(reqnn("copyFrom", copyFrom)); 075 language = copyFrom.language; 076 addBeanTypes = copyFrom.addBeanTypes; 077 rdfSerializer = copyFrom.rdfSerializer; 078 } 079 080 /** 081 * Copy constructor. 082 * 083 * @param copyFrom The bean to copy from. 084 * <br>Cannot be <jk>null</jk>. 085 */ 086 protected Builder(RdfStreamSerializer copyFrom) { 087 super(reqnn("copyFrom", copyFrom)); 088 language = copyFrom.language; 089 addBeanTypes = copyFrom.isAddBeanTypes(); 090 rdfSerializer = copyFrom.rdfSerializer; 091 } 092 093 /** 094 * RDF language (e.g. {@link Constants#LANG_RDFTHRIFT}, {@link Constants#LANG_RDFPROTO}). 095 * 096 * @param value The RDF language. 097 * @return This object. 098 */ 099 public SELF language(String value) { 100 language = value; 101 rdfSerializer = null; // Rebuild on next get 102 return self(); 103 } 104 105 @Override 106 public SELF produces(String value) { 107 super.produces(value); 108 return self(); 109 } 110 111 @Override 112 public SELF accept(String value) { 113 super.accept(value); 114 return self(); 115 } 116 117 @Override 118 public SELF addBeanTypes(boolean value) { 119 super.addBeanTypes(value); 120 addBeanTypes = value; 121 rdfSerializer = null; // Rebuild on next get 122 return self(); 123 } 124 125 @Override 126 public RdfStreamSerializer build() { 127 return cache(CACHE).build(RdfStreamSerializer.class); 128 } 129 130 @Override /* Overridden from Context.Builder<?> */ 131 public abstract SELF copy(); 132 133 @Override 134 public HashKey hashKey() { 135 return HashKey.of(super.hashKey(), language); 136 } 137 138 RdfSerializer getRdfSerializer() { 139 if (rdfSerializer == null) 140 rdfSerializer = RdfSerializer.create().language(language).addBeanTypesRdf(addBeanTypes).build(); 141 return rdfSerializer; 142 } 143 } 144 145 /** 146 * Concrete default builder leaf for the non-subclassed {@link RdfStreamSerializer#create()} / {@link RdfStreamSerializer#copy()} path. 147 */ 148 public static final class DefaultBuilder extends Builder<DefaultBuilder> { 149 150 DefaultBuilder() {} 151 152 DefaultBuilder(RdfStreamSerializer copyFrom) { 153 super(copyFrom); 154 } 155 156 DefaultBuilder(Builder<?> copyFrom) { 157 super(copyFrom); 158 } 159 160 @Override /* Overridden from Context.Builder<?> */ 161 public DefaultBuilder copy() { 162 return new DefaultBuilder(this); 163 } 164 } 165 166 /** 167 * Creates a new builder. 168 * 169 * @return A new builder. 170 */ 171 @SuppressWarnings({ 172 "java:S1452", // Builder<?> wildcard return intentional; callers chain via fluent API without needing the concrete type 173 "java:S9149" // Public Juneau DSL factory; hiding parent create() is intentional. 174 }) 175 public static Builder<?> create() { 176 return new DefaultBuilder(); 177 } 178 179 private final String language; 180 private final RdfSerializer rdfSerializer; 181 182 /** 183 * Constructor. 184 * 185 * @param builder The builder. 186 * <br>Cannot be <jk>null</jk>. 187 */ 188 public RdfStreamSerializer(Builder<?> builder) { 189 super(reqnn("builder", builder).produces(getProduces(builder)).accept(getAccept(builder))); 190 language = builder.language; 191 rdfSerializer = builder.getRdfSerializer(); 192 } 193 194 private static String getAccept(Builder<?> builder) { 195 if (builder.getAccept() != null) 196 return builder.getAccept(); 197 return getProduces(builder); 198 } 199 200 private static String getProduces(Builder<?> builder) { 201 if (builder.getProduces() != null) 202 return builder.getProduces(); 203 return switch (builder.language) { 204 case Constants.LANG_RDFTHRIFT -> "application/vnd.apache.thrift.binary"; 205 case Constants.LANG_RDFPROTO -> "application/vnd.apache.protobuf"; 206 default -> "application/octet-stream"; 207 }; 208 } 209 210 @Override 211 public Builder<?> copy() { 212 return new DefaultBuilder(this); 213 } 214 215 @Override 216 public RdfStreamSerializerSession.Builder createSession() { 217 return RdfStreamSerializerSession.create(this); 218 } 219 220 @Override 221 public RdfBeanMeta getRdfBeanMeta(BeanMeta<?> bm) { 222 return rdfSerializer.getRdfBeanMeta(bm); 223 } 224 225 @Override 226 public RdfBeanPropertyMeta getRdfBeanPropertyMeta(BeanPropertyMeta bpm) { 227 return rdfSerializer.getRdfBeanPropertyMeta(bpm); 228 } 229 230 @Override 231 public RdfClassMeta getRdfClassMeta(ClassMeta<?> cm) { 232 return rdfSerializer.getRdfClassMeta(cm); 233 } 234 235 @Override 236 public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) { 237 return rdfSerializer.getXmlBeanMeta(bm); 238 } 239 240 @Override 241 public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) { 242 return rdfSerializer.getXmlBeanPropertyMeta(bpm); 243 } 244 245 @Override 246 public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) { 247 return rdfSerializer.getXmlClassMeta(cm); 248 } 249 250 /** 251 * Returns the RDF language. 252 * 253 * @return The RDF language identifier. 254 */ 255 public String getLanguage() { 256 return language; 257 } 258 259 /** 260 * Returns the delegated RdfSerializer. 261 * 262 * @return The delegated RdfSerializer. 263 */ 264 protected RdfSerializer getRdfSerializer() { 265 return rdfSerializer; 266 } 267 268 /** 269 * Collection format - delegates to RdfSerializer. 270 * 271 * @return The RDF collection format. 272 */ 273 public RdfCollectionFormat getCollectionFormat() { 274 return rdfSerializer.getCollectionFormat(); 275 } 276 277 /** 278 * Juneau namespace - delegates to RdfSerializer. 279 * 280 * @return The Juneau namespace. 281 */ 282 public Namespace getJuneauNs() { 283 return rdfSerializer.getJuneauNs(); 284 } 285 286 /** 287 * Juneau BP namespace - delegates to RdfSerializer. 288 * 289 * @return The Juneau BP namespace. 290 */ 291 public Namespace getJuneauBpNs() { 292 return rdfSerializer.getJuneauBpNs(); 293 } 294 295 /** 296 * Namespaces - delegates to RdfSerializer. 297 * 298 * @return The RDF namespaces. 299 */ 300 public Namespace[] getNamespaces() { 301 return rdfSerializer.getNamespaces(); 302 } 303 304 /** 305 * Add literal types - delegates to RdfSerializer. 306 * 307 * @return True if literal types are added. 308 */ 309 public boolean isAddLiteralTypes() { 310 return rdfSerializer.isAddLiteralTypes(); 311 } 312 313 /** 314 * Add root property - delegates to RdfSerializer. 315 * 316 * @return True if root property is added. 317 */ 318 public boolean isAddRootProp() { 319 return rdfSerializer.isAddRootProp(); 320 } 321 322 /** 323 * Loose collections - delegates to RdfSerializer. 324 * 325 * @return True if loose collections are enabled. 326 */ 327 public boolean isLooseCollections() { 328 return rdfSerializer.isLooseCollections(); 329 } 330 331 /** 332 * Use XML namespaces - delegates to RdfSerializer. 333 * 334 * @return True if XML namespaces are used. 335 */ 336 public boolean isUseXmlNamespaces() { 337 return rdfSerializer.isUseXmlNamespaces(); 338 } 339 340 /** 341 * Auto-detect namespaces - delegates to RdfSerializer. 342 * 343 * @return True if auto-detect namespaces is enabled. 344 */ 345 public boolean isAutoDetectNamespaces() { 346 return rdfSerializer.isAutoDetectNamespaces(); 347 } 348 349 @Override 350 protected boolean isAddBeanTypes() { 351 return rdfSerializer.isAddBeanTypes(); 352 } 353}