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. 016 */ 017package org.apache.juneau.xml; 018 019import static org.apache.juneau.commons.utils.CollectionUtils.*; 020import static org.apache.juneau.commons.utils.ThrowableUtils.*; 021import static org.apache.juneau.commons.utils.Utils.*; 022 023import org.apache.juneau.*; 024import org.apache.juneau.commons.collections.*; 025import org.apache.juneau.commons.reflect.*; 026import org.apache.juneau.xml.annotation.*; 027 028/** 029 * Metadata on bean properties specific to the XML serializers and parsers pulled from the {@link Xml @Xml} annotation 030 * on the bean property. 031 * 032 * <h5 class='section'>See Also:</h5><ul> 033 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlBasics">XML Basics</a> 034 * </ul> 035 */ 036public class XmlBeanPropertyMeta extends ExtendedBeanPropertyMeta { 037 038 /** 039 * Default instance. 040 */ 041 public static final XmlBeanPropertyMeta DEFAULT = new XmlBeanPropertyMeta(); 042 043 private Namespace namespace; 044 private XmlFormat xmlFormat = XmlFormat.DEFAULT; 045 private String childName; 046 private final XmlMetaProvider xmlMetaProvider; 047 048 /** 049 * Constructor. 050 * 051 * @param bpm The metadata of the bean property of this additional metadata. 052 * @param mp XML metadata provider (for finding information about other artifacts). 053 */ 054 public XmlBeanPropertyMeta(BeanPropertyMeta bpm, XmlMetaProvider mp) { 055 super(bpm); 056 this.xmlMetaProvider = mp; 057 058 bpm.getAnnotations(Xml.class).forEach(x -> findXmlInfo(x.inner(), bpm.getClassMeta().getBeanContext().getAnnotationProvider())); 059 060 if (namespace == null) 061 namespace = mp.getXmlClassMeta(bpm.getBeanMeta().getClassMeta()).getNamespace(); 062 } 063 064 private XmlBeanPropertyMeta() { 065 super(null); 066 this.xmlMetaProvider = null; 067 } 068 069 /** 070 * Returns the child element of this property from the {@link Xml#childName} annotation on this bean property. 071 * 072 * @return The child element, or <jk>null</jk> if annotation not specified. 073 */ 074 public String getChildName() { return childName; } 075 076 /** 077 * Returns the XML namespace associated with this bean property. 078 * 079 * <p> 080 * Namespace is determined in the following order of {@link Xml#prefix() @Xml(prefix)} annotation: 081 * <ol> 082 * <li>Bean property field. 083 * <li>Bean getter. 084 * <li>Bean setter. 085 * <li>Bean class. 086 * <li>Bean package. 087 * <li>Bean superclasses. 088 * <li>Bean superclass packages. 089 * <li>Bean interfaces. 090 * <li>Bean interface packages. 091 * </ol> 092 * 093 * @return The namespace associated with this bean property, or <jk>null</jk> if no namespace is associated with it. 094 */ 095 public Namespace getNamespace() { return namespace; } 096 097 /** 098 * Returns the XML format of this property from the {@link Xml#format} annotation on this bean property. 099 * 100 * @return The XML format, or {@link XmlFormat#DEFAULT} if annotation not specified. 101 */ 102 public XmlFormat getXmlFormat() { return xmlFormat; } 103 104 private void findXmlInfo(Xml xml, AnnotationProvider mp) { 105 if (xml == null) 106 return; 107 var bpm = getBeanPropertyMeta(); 108 var cmProperty = bpm.getClassMeta(); 109 var cmBean = bpm.getBeanMeta().getClassMeta(); 110 var name = bpm.getName(); 111 112 var ap = bpm.getClassMeta().getBeanContext().getAnnotationProvider(); 113 var xmls = new MultiList<>( 114 rstream(ap.find(Xml.class, bpm.getBeanMeta().getClassMeta())).map(AnnotationInfo::inner).toList(), 115 reverse(bpm.getAnnotations(Xml.class).map(AnnotationInfo::inner).toList()) 116 ); 117 var schemas = new MultiList<>( 118 rstream(ap.find(XmlSchema.class, bpm.getBeanMeta().getClassMeta())).map(AnnotationInfo::inner).toList(), 119 reverse(bpm.getAnnotations(XmlSchema.class).map(AnnotationInfo::inner).toList()) 120 ); 121 namespace = XmlUtils.findNamespace(xmls, schemas); 122 123 if (xmlFormat == XmlFormat.DEFAULT) 124 xmlFormat = xml.format(); 125 126 boolean isCollection = cmProperty.isCollectionOrArray(); 127 128 String cen = xml.childName(); 129 if ((! cen.isEmpty()) && (! isCollection)) 130 throw bex(cmProperty.inner(), "Annotation error on property ''{0}''. @Xml.childName can only be specified on collections and arrays.", name); 131 132 if (xmlFormat == XmlFormat.COLLAPSED) { 133 if (isCollection) { 134 if (cen.isEmpty() && nn(xmlMetaProvider)) 135 cen = xmlMetaProvider.getXmlClassMeta(cmProperty).getChildName(); 136 if (cen == null || cen.isEmpty()) 137 cen = cmProperty.getElementType().getBeanDictionaryName(); 138 if (cen == null || cen.isEmpty()) 139 cen = name; 140 } else { 141 throw bex(cmBean.inner(), "Annotation error on property ''{0}''. @Xml.format=COLLAPSED can only be specified on collections and arrays.", name); 142 } 143 if (cen.isEmpty() && isCollection) 144 cen = cmProperty.getBeanDictionaryName(); 145 } 146 147 if (! cen.isEmpty()) 148 childName = cen; 149 } 150}